前后端通信-学习笔记-3

Ajax&Fetch与跨域请求

一、Ajax基础

1、认识Ajax

在这里插入图片描述

1.Ajax 是什么
      Ajax 是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的简写

      Ajax 中的异步:可以异步地向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,浏览器可以做自己的事情。直到成功获取响应后,浏览器才开始处理响应数据

      XML(可扩展标记语言)是前后端数据通信时传输数据的一种格式

      XML 现在已经不怎么用了,现在比较常用的是 JSON

      Ajax 其实就是浏览器与服务器之间的一种异步通信方式

      使用 Ajax 可以在不重新加载整个页面的情况下,对页面的某部分进行更新

      ① 注册检测
      ② 搜索提示

      2.搭建 Ajax 开发环境
      Ajax 需要服务器环境,非服务器环境下,很多浏览器无法正常使用 Ajax

      Live Server vscode插件

      windows phpStudy
      Mac MAMP

2、Ajax的基本用法

在这里插入图片描述

// 1.XMLHttpRequest
      // console.log(Ajax);

      // Ajax 想要实现浏览器与服务器之间的异步通信,需要依靠 XMLHttpRequest,它是一个构造函数

      // 不论是 XMLHttpRequest,还是 Ajax,都没有和具体的某种数据格式绑定

      // 2.Ajax 的使用步骤
      // 2.1.创建 xhr 对象
      // const xhr = new XMLHttpRequest();

      // 2.2.监听事件,处理响应
      // 当获取到响应后,会触发 xhr 对象的 readystatechange 事件,可以在该事件中对响应进行处理

      // xhr.addEventListener('readystatechange', () => {}, fasle);

      // xhr.onreadystatechange = () => {
      //   if (xhr.readyState !== 4) return;

      //   // HTTP CODE
      //   // 获取到响应后,响应的内容会自动填充 xhr 对象的属性
      //   // xhr.status:HTTP  200 404
      //   // xhr.statusText:HTTP 状态说明 OK Not Found
      //   if ((xhr.status >= 200) & (xhr.status < 300) || xhr.status === 304) {
      //     // console.log('正常使用响应数据');
      //     console.log(xhr.responseText);
      //   }
      // };

      // readystatechange 事件也可以配合 addEventListener 使用,不过要注意,IE6~8 不支持 addEventListener
      // 为了兼容性,readystatechange 中不使用 this,而是直接使用 xhr
      // 由于兼容性的原因,最好放在 open 之前

      // readystatechange 事件监听 readyState 这个状态的变化
      // 它的值从 0 ~ 4,一共 5 个状态
      // 0:未初始化。尚未调用 open()
      // 1:启动。已经调用 open(),但尚未调用 send()
      // 2:发送。已经调用 send(),但尚未接收到响应
      // 3:接收。已经接收到部分响应数据
      // 4:完成。已经接收到全部响应数据,而且已经可以在浏览器中使用了

      // 2.3.准备发送请求
      // xhr.open(
      //   'HTTP 方法 GET、POST、PUT、DELETE',
      //   '地址 URL https://www.imooc.com/api/http/search/suggest?words=js ./index.html ./index.xml ./index.txt',
      //   true
      // );

      // 调用 open 并不会真正发送请求,而只是做好发送请求前的准备工作

      // 2.3.发送请求
      // 调用 send() 正式发送请求

      // send() 的参数是通过请求体携带的数据
      // xhr.send(null);

      // 3.使用 Ajax 完成前后端通信
      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';

      const xhr = new XMLHttpRequest();
      xhr.onreadystatechange = () => {
        if (xhr.readyState !== 4) return;

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          console.log(xhr.responseText);
          console.log(typeof xhr.responseText);
        }
      };
      xhr.open('GET', url, true);
      xhr.send(null);

3、GET请求

在这里插入图片描述

// 1.携带数据
      // GET 请求不能通过请求体携带数据,但可以通过请求头携带
      // const url =
      //   'https://www.imooc.com/api/http/search/suggest?words=js&username=alex&age=18';
      // const xhr = new XMLHttpRequest();

      // xhr.onreadystatechange = () => {
      //   if (xhr.readyState != 4) return;

      //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      //     console.log(xhr.responseText);
      //   }
      // };

      // xhr.open('GET', url, true);

      // xhr.send(null);

      // 不会报错,但不会发送数据
      // xhr.send('sex=male');

      // 2.数据编码
      // 如果携带的数据是非英文字母的话,比如说汉字,就需要编码之后再发送给后端,不然会造成乱码问题
      // 可以使用 encodeURIComponent() 编码
      const url = `https://www.imooc.com/api/http/search/suggest?words=${encodeURIComponent(
        '前端'
      )}`;
      const xhr = new XMLHttpRequest();

      xhr.onreadystatechange = () => {
        if (xhr.readyState != 4) return;

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          console.log(xhr.responseText);
        }
      };

      xhr.open('GET', url, true);

      xhr.send(null);

4、POST请求

 // 1.携带数据
      // POST 请求主要通过请求体携带数据,同时也可以通过请求头携带
      // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      // const xhr = new XMLHttpRequest();

      // xhr.onreadystatechange = () => {
      //   if (xhr.readyState != 4) return;

      //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      //     console.log(xhr.responseText);
      //   }
      // };

      // xhr.open('POST', url, true);

      // 如果想发送数据,直接写在 send() 的参数位置,一般是字符串
      // xhr.send('username=alex&age=18');

      // 不能直接传递对象,需要先将对象转换成字符串的形式
      // xhr.send({
      //   username: 'alex',
      //   age: 18
      // });
      // [object Object]

      // 2.数据编码
      // xhr.send(`username=${encodeURIComponent('张三')}&age=18`);

二、JSON

1、认识JSON

// 1.JSON 是什么
      // Ajax 发送和接收数据的一种格式
      // XML
      // username=alex&age=18
      // JSON

      // {"code":200,"data":[{"word":"jsp"},{"word":"js"},{"word":"json"},{"word":"js \u5165\u95e8"},{"word":"jstl"}]}
      // HTML/XML

      // JSON 全称是 JavaScript Object Notation

      // 2.为什么需要 JSON
      //  JSON 有 3 种形式,每种形式的写法都和 JS 中的数据类型很像,可以很轻松的和 JS 中的数据类型互相转换

      // JS->JSON->PHP/Java
      // PHP/Java->JSON->JS

2、JSON的三种形式

在这里插入图片描述

// 1.简单值形式
      // .json

      // JSON 的简单值形式就对应着 JS 中的基础数据类型
      // 数字、字符串、布尔值、null

      // 注意事项
      // ① JSON 中没有 undefined 值
      // ② JSON 中的字符串必须使用双引号
      // ③ JSON 中是不能注释的

      // 2.对象形式
      // JSON 的对象形式就对应着 JS 中的对象

      // 注意事项
      // JSON 中对象的属性名必须用双引号,属性值如果是字符串也必须用双引号
      // JSON 中只要涉及到字符串,就必须使用双引号
      // 不支持 undefined

      // 3.数组形式
      // JSON 的数组形式就对应着 JS 中的数组

      // [1, "hi", null]

      // 注意事项
      // 数组中的字符串必须用双引号
      // JSON 中只要涉及到字符串,就必须使用双引号
      // 不支持 undefined

3、JSON的常用方法

在这里插入图片描述

// 1.JSON.parse()
      // JSON.parse() 可以将 JSON 格式的字符串解析成 JS 中的对应值
      // 一定要是合法的 JSON 字符串,否则会报错
      // const xhr = new XMLHttpRequest();

      // xhr.onreadystatechange = () => {
      //   if (xhr.readyState != 4) return;

      //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      //     console.log(xhr.responseText);
      //     console.log(typeof xhr.responseText);

      //     console.log(JSON.parse(xhr.responseText));
      //     // console.log(JSON.parse(xhr.responseText).data);
      //   }
      // };

      // // xhr.open('GET', './plain.json', true);
      // // xhr.open('GET', './obj.json', true);
      // xhr.open('GET', './arr.json', true);

      // // xhr.open(
      // //   'GET',
      // //   'https://www.imooc.com/api/http/search/suggest?words=js',
      // //   true
      // // );

      // xhr.send(null);

      // 2.JSON.stringify()
      // JSON.stringify() 可以将 JS 的基本数据类型、对象或者数组转换成 JSON 格式的字符串
      // console.log(
      //   JSON.stringify({
      //     username: 'alex',
      //     age: 18
      //   })
      // );

      // const xhr = new XMLHttpRequest();

      // xhr.open(
      //   'POST',
      //   'https://www.imooc.com/api/http/search/suggest?words=js',
      //   true
      // );
      // xhr.send(
      //   JSON.stringify({
      //     username: 'alex',
      //     age: 18
      //   })
      // );

      // 3.使用 JSON.parse() 和 JSON.stringify() 封装 localStorage
      import { get, set, remove, clear } from './storage.js';

      set('username', 'alex');
      console.log(get('username'));

      set('zs', {
        name: '张三',
        age: 18
      });
      console.log(get('zs'));

      remove('username');
      clear();
const storage = window.localStorage;

// 设置
const set = (key, value) => {
  // {
  //   username: 'alex'
  // }
  storage.setItem(key, JSON.stringify(value));
};

// 获取
const get = key => {
  // 'alex'
  // {
  //   "username": "alex"
  // }
  return JSON.parse(storage.getItem(key));
};

// 删除
const remove = key => {
  storage.removeItem(key);
};

// 清空
const clear = () => {
  storage.clear();
};

export { set, get, remove, clear };

三、跨域

1、认识跨域

在这里插入图片描述

 1.跨域是什么

      同域,不是跨域
      const url = './index.html';

      http://127.0.0.1:5500

      不同域,跨域,被浏览器阻止
      const url = 'https://www.imooc.com';
      const xhr = new XMLHttpRequest();

      Access to XMLHttpRequest at 'https://www.imooc.com/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

      向一个域发送请求,如果要请求的域和当前域是不同域,就叫跨域
      不同域之间的请求,就是跨域请求

      xhr.onreadystatechange = () => {
        if (xhr.readyState != 4) return;

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          console.log(xhr.responseText);
        }
      };

      xhr.open('GET', url, true);

      xhr.send(null);

      2.什么是不同域,什么是同域
      https(协议)://www.imooc.com(域名):443(端口号)/course/list(路径)

      协议、域名、端口号,任何一个不一样,就是不同域
      与路径无关,路径一不一样无所谓

      不同域
      https://www.imooc.com:443/course/list
      http://www.imooc.com:80/course/list

      http://www.imooc.com:80/course/list
      http://m.imooc.com:80/course/list
      http://imooc.com:80/course/list

      同域
      http://imooc.com:80
      http://imooc.com:80/course/list

      3.跨域请求为什么会被阻止
      阻止跨域请求,其实是浏览器本身的一种安全策略--同源策略

      其他客户端或者服务器都不存在跨域被阻止的问题

      4.跨域解决方案
      ① CORS 跨域资源共享
      ② JSONP

      优先使用 CORS 跨域资源共享,如果浏览器不支持 CORS 的话,再使用 JSONP

2、CORS跨域资源共享

在这里插入图片描述

1.CORS 是什么
      Access-Control-Allow-Origin: *
      表明允许所有的域名来跨域请求它,* 是通配符,没有任何限制

      只允许指定域名的跨域请求
      Access-Control-Allow-Origin: http://127.0.0.1:5500

      2.使用 CORS 跨域的过程
      ① 浏览器发送请求
      ② 后端在响应头中添加 Access-Control-Allow-Origin 头信息
      ③ 浏览器接收到响应
      ④ 如果是同域下的请求,浏览器不会额外做什么,这次前后端通信就圆满完成了
      ⑤ 如果是跨域请求,浏览器会从响应头中查找是否允许跨域访问
      ⑥ 如果允许跨域,通信圆满完成
      ⑦ 如果没找到或不包含想要跨域的域名,就丢弃响应结果

      3.CORS 的兼容性
      IE10 及以上版本的浏览器可以正常使用 CORS

      https://caniuse.com/

3、JSONP

  1.JSONP 的原理
      // script 标签跨域不会被浏览器阻止
      // JSONP 主要就是利用 script 标签,加载跨域文件

      // 2.使用 JSONP 实现跨域
      // 服务器端准备好 JSONP 接口
      // https://www.imooc.com/api/http/jsonp?callback=handleResponse

      // 手动加载 JSONP 接口或动态加载 JSONP 接口
      const script = document.createElement('script');
      script.src =
        'https://www.imooc.com/api/http/jsonp?callback=handleResponse';
      document.body.appendChild(script);

四、XHR对象

1、XHR的属性

在这里插入图片描述

 1.responseType 和 response 属性
      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      const xhr = new XMLHttpRequest();

      xhr.onreadystatechange = () => {
        if (xhr.readyState != 4) return;

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          // 文本形式的响应内容
          // responseText 只能在没有设置 responseType 或者 responseType = '' 或 'text' 的时候才能使用
          // console.log('responseText:', xhr.responseText);

          // 可以用来替代 responseText
          console.log('response:', xhr.response);
          // console.log(JSON.parse(xhr.responseText));
        }
      };
      xhr.open('GET', url, true);

      // xhr.responseType = '';
      // xhr.responseType = 'text';
      xhr.responseType = 'json';

      xhr.send(null);

      IE6~9 不支持,IE10 开始支持

      2.timeout 属性
      设置请求的超时时间(单位 ms)
      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      const xhr = new XMLHttpRequest();

      xhr.onreadystatechange = () => {
        if (xhr.readyState != 4) return;

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          console.log(xhr.response);
        }
      };

      xhr.open('GET', url, true);

      xhr.timeout = 10000;

      xhr.send(null);

      IE6~7 不支持,IE8 开始支持

      3.withCredentials 属性
      指定使用 Ajax 发送请求时是否携带 Cookie

      使用 Ajax 发送请求,默认情况下,同域时,会携带 Cookie;跨域时,不会
      xhr.withCredentials = true;
      最终能否成功跨域携带 Cookie,还要看服务器同不同意

      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      // const url = './index.html';

      const xhr = new XMLHttpRequest();

      xhr.onreadystatechange = () => {
        if (xhr.readyState != 4) return;

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          console.log(xhr.response);
        }
      };

      xhr.open('GET', url, true);

      xhr.withCredentials = true;

      xhr.send(null);

      IE6~9 不支持,IE10 开始支持

2、XHR的方法

在这里插入图片描述

 // 1.abort()
      // 终止当前请求
      // 一般配合 abort 事件一起使用
      // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';

      // const xhr = new XMLHttpRequest();

      // xhr.onreadystatechange = () => {
      //   if (xhr.readyState != 4) return;

      //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      //     console.log(xhr.response);
      //   }
      // };

      // xhr.open('GET', url, true);

      // xhr.send(null);

      // xhr.abort();

      // 2.setRequestHeader()
      // 可以设置请求头信息
      // xhr.setRequestHeader(头部字段的名称, 头部字段的值);
      // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      const url = 'https://www.imooc.com/api/http/json/search/suggest?words=js';

      const xhr = new XMLHttpRequest();

      xhr.onreadystatechange = () => {
        if (xhr.readyState != 4) return;

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          console.log(xhr.response);
        }
      };
      xhr.open('POST', url, true);

      // 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送的数据是什么格式的
      // xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      xhr.setRequestHeader('Content-Type', 'application/json');

      // xhr.send(null);
      // xhr.send('username=alex&age=18');
      xhr.send(
        JSON.stringify({
          username: 'alex'
        })
      );

3、XHR的事件

在这里插入图片描述

// 1.load 事件
      // 响应数据可用时触发
      // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      // const xhr = new XMLHttpRequest();

      // // xhr.onload = () => {
      // //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      // //     console.log(xhr.response);
      // //   }
      // // };
      // xhr.addEventListener(
      //   'load',
      //   () => {
      //     if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      //       console.log(xhr.response);
      //     }
      //   },
      //   false
      // );

      // xhr.open('GET', url, true);

      // xhr.send(null);

      // IE6~8 不支持 load 事件

      // 2.error 事件
      // 请求发生错误时触发

      // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      // const url = 'https://www.iimooc.com/api/http/search/suggest?words=js';
      // const xhr = new XMLHttpRequest();

      // xhr.addEventListener(
      //   'load',
      //   () => {
      //     if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      //       console.log(xhr.response);
      //     }
      //   },
      //   false
      // );
      // xhr.addEventListener(
      //   'error',
      //   () => {
      //     console.log('error');
      //   },
      //   false
      // );

      // xhr.open('GET', url, true);

      // xhr.send(null);

      // IE10 开始支持

      // 3.abort 事件
      // 调用 abort() 终止请求时触发
      // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';

      // const xhr = new XMLHttpRequest();

      // xhr.addEventListener(
      //   'load',
      //   () => {
      //     if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      //       console.log(xhr.response);
      //     }
      //   },
      //   false
      // );
      // xhr.addEventListener(
      //   'abort',
      //   () => {
      //     console.log('abort');
      //   },
      //   false
      // );

      // xhr.open('GET', url, true);

      // xhr.send(null);

      // xhr.abort();

      // IE10 开始支持

      // 4.timeout 事件
      // 请求超时后触发
      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';

      const xhr = new XMLHttpRequest();

      xhr.addEventListener(
        'load',
        () => {
          if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
            console.log(xhr.response);
          }
        },
        false
      );
      xhr.addEventListener(
        'timeout',
        () => {
          console.log('timeout');
        },
        false
      );

      xhr.open('GET', url, true);

      xhr.timeout = 10;

      xhr.send(null);

      // IE8 开始支持

五、Ajax进阶

1、FormData

在这里插入图片描述

 // 1.使用 Ajax 提交表单

      // 2.FormData 的基本用法
      // 通过 HTML 表单元素创建 FormData 对象
      // const fd = new FormData(表单元素);
      // xhr.send(fd);

      // 通过 append() 方法添加数据
      // const fd = new FormData(表单元素);
      // fd.append('age', 18);
      // fd.append('sex', 'male');
      // xhr.send(fd);

      // IE10 及以上可以支持

      const login = document.getElementById('login');
      // console.log(login.username);
      // console.log(login.password);
      const { username, password } = login;
      const btn = document.getElementById('submit');
      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';

      btn.addEventListener(
        'click',
        e => {
          // 阻止表单自动提交
          e.preventDefault();

          // 表单数据验证

          // 发送 Ajax 请求
          const xhr = new XMLHttpRequest();

          xhr.addEventListener(
            'load',
            () => {
              if (
                (xhr.status >= 200 && xhr.status < 300) ||
                xhr.status === 304
              ) {
                console.log(xhr.response);
              }
            },
            false
          );

          xhr.open('POST', url, true);

          // 组装数据
          // const data = `username=${username.value}&password=${password.value}`;

          // FormData 可用于发送表单数据
          const data = new FormData(login);
          // data.append('age', 18);
          // data.append('sex', 'male');
          // console.log(data);
          // for (const item of data) {
          //   console.log(item);
          // }

          // xhr.setRequestHeader(
          //   'Content-Type',
          //   'application/x-www-form-urlencoded'
          // );

          xhr.send(data);

          // xhr.send('username=alex&password=12345');
        },
        false
      );

2、封装Ajax

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。

import { ajax, get, getJSON, post } from './ajax/index.js';

      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      // const url = 'https://www.iimooc.com/api/http/search/suggest?words=js';

      // const url = './414.html';

      const xhr = ajax(url, {
        method: 'GET',
        params: { username: 'alex' },
        data: {
          age: 18
        },
        responseType: 'json',
        // timeoutTime: 10,

        success(response) {
          console.log(response);
        },
        httpCodeError(err) {
          console.log('http code error', err);
        },
        error(xhr) {
          console.log('error', xhr);
        },
        abort(xhr) {
          console.log('abort', xhr);
        },
        timeout(xhr) {
          console.log('timeout', xhr);
        }
      });

      xhr.abort();
import {
  HTTP_GET,
  CONTENT_TYPE_FORM_URLENCODED,
  CONTENT_TYPE_JSON
} from './constants.js';

// 工具函数
import { serialize, addURLData, serializeJSON } from './utils.js';

// 默认参数
import DEFAULTS from './defaults.js';

// Ajax 类
class Ajax {
  constructor(url, options) {
    this.url = url;
    this.options = Object.assign({}, DEFAULTS, options);

    // 初始化
    this.init();
  }

  // 初始化
  init() {
    const xhr = new XMLHttpRequest();

    this.xhr = xhr;

    // 绑定响应事件处理程序
    this.bindEvents();

    xhr.open(this.options.method, this.url + this.addParam(), true);

    // 设置 responseType
    this.setResponseType();

    // 设置跨域是否携带 cookie
    this.setCookie();

    // 设置超时
    this.setTimeout();

    // 发送请求
    this.sendData();
  }

  // 绑定响应事件处理程序
  bindEvents() {
    const xhr = this.xhr;

    const { success, httpCodeError, error, abort, timeout } = this.options;

    // load
    xhr.addEventListener(
      'load',
      () => {
        if (this.ok()) {
          success(xhr.response, xhr);
        } else {
          httpCodeError(xhr.status, xhr);
        }
      },
      false
    );

    // error
    // 当请求遇到错误时,将触发 error 事件
    xhr.addEventListener(
      'error',
      () => {
        error(xhr);
      },
      false
    );

    // abort
    xhr.addEventListener(
      'abort',
      () => {
        abort(xhr);
      },
      false
    );

    // timeout
    xhr.addEventListener(
      'timeout',
      () => {
        timeout(xhr);
      },
      false
    );
  }

  // 检测响应的 HTTP 状态码是否正常
  ok() {
    const xhr = this.xhr;
    return (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304;
  }

  // 在地址上添加数据
  addParam() {
    const { params } = this.options;

    if (!params) return '';

    return addURLData(this.url, serialize(params));
  }

  // 设置 responseType
  setResponseType() {
    this.xhr.responseType = this.options.responseType;
  }

  // 设置跨域是否携带 cookie
  setCookie() {
    if (this.options.withCredentials) {
      this.xhr.withCredentials = true;
    }
  }

  // 设置超时
  setTimeout() {
    const { timeoutTime } = this.options;

    if (timeoutTime > 0) {
      this.xhr.timeout = timeoutTime;
    }
  }

  // 发送请求
  sendData() {
    const xhr = this.xhr;

    if (!this.isSendData()) {
      return xhr.send(null);
    }

    let resultData = null;
    const { data } = this.options;

    // 发送 FormData 格式的数据
    if (this.isFormData()) {
      resultData = data;
    } else if (this.isFormURLEncodedData()) {
      // 发送 application/x-www-form-urlencoded 格式的数据

      this.setContentType(CONTENT_TYPE_FORM_URLENCODED);
      resultData = serialize(data);
    } else if (this.isJSONData()) {
      // 发送 application/json 格式的数据

      this.setContentType(CONTENT_TYPE_JSON);
      resultData = serializeJSON(data);
    } else {
      // 发送其他格式的数据

      this.setContentType();
      resultData = data;
    }

    xhr.send(resultData);
  }

  // 是否需要使用 send 发送数据
  isSendData() {
    const { data, method } = this.options;

    if (!data) return false;

    if (method.toLowerCase() === HTTP_GET.toLowerCase()) return false;

    return true;
  }

  // 是否发送 FormData 格式的数据
  isFormData() {
    return this.options.data instanceof FormData;
  }

  // 是否发送 application/x-www-form-urlencoded 格式的数据
  isFormURLEncodedData() {
    return this.options.contentType
      .toLowerCase()
      .includes(CONTENT_TYPE_FORM_URLENCODED);
  }

  // 是否发送 application/json 格式的数据
  isJSONData() {
    return this.options.contentType.toLowerCase().includes(CONTENT_TYPE_JSON);
  }

  // 设置 Content-Type
  setContentType(contentType = this.options.contentType) {
    if (!contentType) return;

    this.xhr.setRequestHeader('Content-Type', contentType);
  }

  // 获取 XHR 对象
  getXHR() {
    return this.xhr;
  }
}

export default Ajax;
export const HTTP_GET = 'GET';
export const CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded';
export const CONTENT_TYPE_JSON = 'application/json';

// 常量
import { HTTP_GET, CONTENT_TYPE_FORM_URLENCODED } from './constants.js';

// 默认参数
const DEFAULTS = {
  method: HTTP_GET,
  // 请求头携带的数据
  params: null,
  // params: {
  //   username: 'alex',
  //   age: 18
  // }
  // username=alex&age=18
  // 请求体携带的数据
  data: null,
  // data: {
  //   username: 'alex',
  //   age: 18
  // }
  // data: FormData 数据

  contentType: CONTENT_TYPE_FORM_URLENCODED,
  responseType: '',
  timeoutTime: 0,
  withCredentials: false,

  // 方法
  success() {},
  httpCodeError() {},
  error() {},
  abort() {},
  timeout() {}
};

export default DEFAULTS;

import Ajax from './ajax.js';

const ajax = (url, options) => {
  return new Ajax(url, options).getXHR();
};

const get = (url, options) => {
  return ajax(url, { ...options, method: 'GET' });
};

const getJSON = (url, options) => {
  return ajax(url, { ...options, method: 'GET', responseType: 'json' });
};

const post = (url, options) => {
  return ajax(url, { ...options, method: 'POST' });
};

export { ajax, get, getJSON, post };
// 工具函数

// 数据序列化成 urlencoded 格式的字符串
const serialize = param => {
  const results = [];

  for (const [key, value] of Object.entries(param)) {
    results.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
  }

  // ['username=alex', 'age=18'];

  return results.join('&');
};

// 数据序列化成 JSON 格式的字符串
const serializeJSON = param => {
  return JSON.stringify(param);
};

// 给 URL 添加参数
// www.imooc.com?words=js&
const addURLData = (url, data) => {
  if (!data) return '';

  const mark = url.includes('?') ? '&' : '?';

  return `${mark}${data}`;
};

export { serialize, addURLData, serializeJSON };

六、Ajax应用

1、搜索提示

 <body>
    <input id="search" type="text" />
    <ul id="result"></ul>

    <script type="module">
      import { getJSON } from './ajax/index.js';

      const searchInput = document.getElementById('search');
      const resultList = document.getElementById('result');

      const url = 'https://www.imooc.com/api/http/search/suggest?words=';

      const handleInputEvent = () => {
        if (searchInput.value.trim() !== '') {
          getJSON(`${url}${searchInput.value}`)
            .then(response => {
              console.log(response);
              // [{word: "jsp"}]

              let html = '';
              for (const item of response.data) {
                html += `<li>${item.word}</li>`;
              }

              resultList.innerHTML = html;

              resultList.style.display = '';

              // resultList.innerHTML = '<li>jsp</li><li>js</li>';
            })
            .catch(err => {
              console.log(err);
            });
        } else {
          resultList.innerHTML = '';
          resultList.style.display = 'none';
        }
      };

      let timer = null;
      // IE9 开始支持
      searchInput.addEventListener(
        'input',
        () => {
          // handleInputEvent();

          if (timer) {
            clearTimeout(timer);
          }
          // jsa
          timer = setTimeout(handleInputEvent, 500);
        },
        false
      );
    </script>
  </body>

2、二级菜单

  <body>
    <ul id="menu" class="menu">
      <!-- <li class="menu-item" data-key="hot" data-done="done">
        <span>热门</span>
        <div class="menu-content">
          <p><img class="menu-loading" src="./loading.gif" alt="加载中" /></p>
        </div>
      </li> -->
    </ul>

    <script type="module">
      // https://www.imooc.com/api/mall-PC/index/menu/hot
      // https://www.imooc.com/api/mall-PC/index/menu

      import { getJSON } from './ajax/index.js';
      const menuURL = 'https://www.imooc.com/api/mall-PC/index/menu';
      const menuEl = document.getElementById('menu');

      getJSON(menuURL)
        .then(repsonse => {
          // console.log(repsonse);

          let html = '';

          for (const item of repsonse.data) {
            html += `
              <li class="menu-item" data-key="${item.key}">
                <span>${item.title}</span>
                <div class="menu-content">
                  <p><img class="menu-loading" src="./loading.gif" alt="加载中" /></p>
                </div>
              </li>
            `;
          }

          menuEl.innerHTML = html;

          // [{key: "hot", title: "热门出发地", subTitles: Array(5)}]

          // ...
        })
        .then(() => {
          const items = menuEl.querySelectorAll('.menu-item');

          for (const item of items) {
            item.addEventListener(
              'mouseenter',
              () => {
                // console.log(item.getAttribute('data-key'));

                // IE11 开始支持
                // console.log(item.dataset.key);

                if (item.dataset.done === 'done') return;

                getJSON(
                  `https://www.imooc.com/api/mall-PC/index/menu/${item.dataset.key}`
                )
                  .then(repsonse => {
                    // console.log(repsonse);

                    // [{title: "内地热门城市", cities: Array(27)}]

                    item.dataset.done = 'done';

                    let html = '';

                    for (const item of repsonse.data) {
                      html += `<p>${item.title}</p>`;
                    }

                    item.querySelector('.menu-content').innerHTML = html;
                  })
                  .catch(err => {
                    console.log(err);
                  });
              },
              false
            );
          }
        })
        .catch(err => {
          console.log(err);
        });
    </script>
  </body>

3、多个Ajax请求的并发执行

<body>
    <div id="loading-page" class="loading-page">
      <img class="loading-img" src="./loading.gif" alt="加载中" />
    </div>

    <div id="ad" class="ad"></div>

    <ul id="menu" class="menu">
      <!-- <li class="menu-item" data-key="hot" data-done="done">
        <span>热门</span>
        <div class="menu-content">
          <p><img class="menu-loading" src="./loading.gif" alt="加载中" /></p>
        </div>
      </li> -->
    </ul>
    <script type="module">
      import { getJSON } from './ajax/index.js';

      const menuURL = 'https://www.imooc.com/api/mall-PC/index/menu';
      const adURL = 'https://www.imooc.com/api/mall-PC/index/ad';

      const loadingPageEl = document.getElementById('loading-page');
      const adEl = document.getElementById('ad');
      const menuEl = document.getElementById('menu');

      const p1 = getJSON(menuURL)
        .then(repsonse => {
          // console.log(repsonse);

          let html = '';

          for (const item of repsonse.data) {
            html += `
              <li class="menu-item" data-key="${item.key}">
                <span>${item.title}</span>
                <div class="menu-content">
                  <p><img class="menu-loading" src="./loading.gif" alt="加载中" /></p>
                </div>
              </li>
            `;
          }

          menuEl.innerHTML = html;

          // [{key: "hot", title: "热门出发地", subTitles: Array(5)}]

          // ...
        })
        .then(() => {
          const items = menuEl.querySelectorAll('.menu-item');

          for (const item of items) {
            item.addEventListener(
              'mouseenter',
              () => {
                // console.log(item.getAttribute('data-key'));

                // IE11 开始支持
                // console.log(item.dataset.key);

                if (item.dataset.done === 'done') return;

                getJSON(
                  `https://www.imooc.com/api/mall-PC/index/menu/${item.dataset.key}`
                )
                  .then(repsonse => {
                    // console.log(repsonse);

                    // [{title: "内地热门城市", cities: Array(27)}]

                    item.dataset.done = 'done';

                    let html = '';

                    for (const item of repsonse.data) {
                      html += `<p>${item.title}</p>`;
                    }

                    item.querySelector('.menu-content').innerHTML = html;
                  })
                  .catch(err => {
                    console.log(err);
                  });
              },
              false
            );
          }
        })
        .catch(err => {
          console.log(err);
        });

      const p2 = getJSON(adURL)
        .then(response => {
          // console.log(response);
          // [{ url: 'http://alimc.img.imooc.com/class/' }];

          let html = '';
          for (const item of response.data) {
            html += `<img src="${item.url}" alt="" />`;
          }
          adEl.innerHTML = html;
        })
        .catch(err => {
          console.log(err);
        });

      Promise.all([p1, p2]).then(() => {
        // loadingPageEl.style.display = 'none';

        // IE10 开始支持
        loadingPageEl.classList.add('none');
        // loadingPageEl.classList.remove('none');
      });
    </script>
  </body>

七、Ajax扩展

1、axios

在这里插入图片描述

  <body>
    <!-- <script src="https://unpkg.com/axios/dist/axios.min.js"></script> -->
    <script src="https://unpkg.com/axios@0.19.2/dist/axios.min.js"></script>

    <script>
      // 1.axios 是什么
      // axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中
      // 第三方 Ajax 库

      // http://www.axios-js.com/zh-cn/docs/

      // 2.axios 的基本用法
      // 引入 axios
      // console.log(axios);

      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      // axios(url, {
      //   method: 'post',

      //   // 请求时的头信息
      //   headers: {
      //     'Content-Type': 'application/x-www-form-urlencoded'
      //     // 'Content-Type': 'application/json'
      //   },

      //   // 通过请求头携带的数据
      //   params: {
      //     username: 'alex'
      //   },

      //   // 通过请求体携带的数据

      //   // application/json
      //   // data: {
      //   //   age: 18,
      //   //   sex: 'male'
      //   // }

      //   // application/x-www-form-urlencoded
      //   data: 'age=18&sex=male'

      //   // timeout: 10

      //   // withCredentials: true
      // })
      //   .then(response => {
      //     console.log(response);
      //     console.log(response.data.data);
      //   })
      //   .catch(err => {
      //     console.log(err);
      //   });

      // axios
      //   .get(url, {
      //     params: {
      //       username: 'alex'
      //     }
      //   })
      //   .then(response => {
      //     console.log(response);
      //   });

      // axios
      //   .post(url, 'username=alex&age=18')
      //   .then(response => {
      //     console.log(response);
      //   })
      //   .catch(err => {
      //     console.log(err);
      //   });

      axios
        .post('https://www.imooc.com/api/http/json/search/suggest?words=js', {
          username: 'alex'
        })
        .then(response => {
          console.log(response);
        })
        .catch(err => {
          console.log(err);
        });

      // axios.put()
      // axios.delete()
    </script>
  </body>

2、Fetch

<body>
    <script>
      // 1.Fetch 是什么
      // Fetch 也是前后端通信的一种方式
      // Fetch 是 Ajax(XMLHttpRequest)的一种替代方案,它是基于 Promise 的

      // Ajax 的兼容性比 Fetch 好

      // abort timeout

      // 2.Fetch 的基本用法
      // console.log(fetch);
      // console.log(ajax);

      // fetch() 调用后返回 Promise 对象
      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';

      // body: (...)
      // bodyUsed: false
      // ok: true
      // status: 200
      // statusText: "OK"
      // type: "cors"
      // url: "https://www.im

      // 第二个参数是对象,用来配置 fetch
      const fd = new FormData();
      fd.append('username', 'alex');
      fetch(url, {
        method: 'post',
        // body: null
        // body: 'username=alex&age=18',
        // body: JSON.stringify({ username: 'alex' })
        body: fd,
        // headers: {
        //   // 'Content-Type': 'application/x-www-form-urlencoded'
        //   'Content-Type': 'application/json'
        // }
        mode: 'cors'
        // credentials:'include'
      })
        .then(response => {
          console.log(response);

          // body/bodyUsed
          // body 只能读一次,读过之后就不让再读了

          // ok
          // 如果为 true,表示可以读取数据,不用再去判断 HTTP 状态码了

          if (response.ok) {
            // console.log(response.json());

            return response.json();
            // return response.text();
          } else {
            throw new Error(`HTTP CODE 异常 ${response.status}`);
          }
        })
        .then(data => {
          console.log(data);
        })
        .catch(err => {
          console.log(err);
        });
    </script>
  </body>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值