前端学习笔记之 HTTP协议、存储、Ajax(十四)

week14 HTTP协议、存储、Ajax

前端数据交互与HTTP协议

前后端通信

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>初识前后端通信</title>
  </head>
  <body>
    <script>
      // 1.前后端通信是什么
      // 前端和后端数据交互的过程
      // 浏览器和服务器之间数据交互的过程

      // 2.后端向前端发送数据
      // 访问页面

      // 3.前端向后端发送数据
      // 用户注册
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>前后端通信的过程与概念解释</title>
  </head>
  <body>
    <script>
      // 1.前后端通信的过程
      // 前后端的通信是在‘请求-响应’中完成的

      // 2.概念解释
      // 前端:浏览器端
      // 客户端:只要能和服务器通信的就叫客户端
      // 命令行工具
      // curl https:www.imooc.com

      // 后端:服务器端
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>前后端的通信方式</title>
    <link rel="stylesheet" href="./index.css" />
  </head>
  <body>
    <a href="https://www.imooc.com">慕课网</a>

    <form action="https://www.imooc.com" method="post">
      <input type="text" name="username" placeholder="用户名" />
      <input type="password" name="password" placeholder="密码" />
      <input type="submit" value="注册" />
    </form>

    <img src="./index.png" alt="" />

    <script src="./index.js"></script>
    <script>
      // 1.使用浏览器访问网页
      // 在浏览器地址栏输入网址,按下回车

      // 2.HTML 的标签
      // 浏览器在解析 HTML 标签的时候,遇到一些特殊的标签,会再次向服务器发送请求
      // link/img/script/iframe

      // 还有一些标签,浏览器解析的时候,不会向服务器发送请求,但是用户可以使用他们向服务器发送请求
      // a/form

      // 3.Ajax 和 Fetch
    </script>
  </body>
</html>

HTTP协议

初识 HTTP

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>初识 HTTP</title>
  </head>
  <body>
    <script>
      // 1.HTTP 是什么
      // HyperText Transfer Protocol
      // 超文本传输协议
      // HTML:超文本标记语言

      // 超文本:原先一个个单一的文本,通过超链接将其联系起来。由原先的单一的文本变成了可无限延伸、扩展的超级文本、立体文本

      // HTML、JS、CSS、图片、字体、音频、视频等等文件,都是通过 HTTP(超文本传输协议) 在服务器和浏览器之间传输

      // 每一次前后端通信,前端需要主动向后端发出请求,后端接收到前端的请求后,可以给出响应
      // HTTP 是一个请求-响应协议

      // 2.HTTP 请求响应的过程
      // https://www.imooc.com
    </script>
  </body>
</html>

HTTP报文

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>HTTP 报文</title>
  </head>
  <body>
    <form action="https://www.imooc.com" method="get">
      <input type="text" name="username" placeholder="用户名" />
      <input type="password" name="password" placeholder="密码" />
      <input type="submit" value="注册" />
    </form>

    <form action="https://www.imooc.com" method="post">
      <input type="text" name="username" placeholder="用户名" />
      <input type="password" name="password" placeholder="密码" />
      <input type="submit" value="注册" />
    </form>

    <script>
      // 1.HTTP 报文是什么
      // 浏览器向服务器发送请求时,请求本身就是信息,叫请求报文
      // 服务器向浏览器发送响应时传输的信息,叫响应报文

      // 2.HTTP 报文格式
      // 请求
      // 请求头:起始行+首部
      // 请求体

      // GET 请求,没有请求体,数据通过请求头携带
      // POST 请求,有请求体,数据通过请求体携带

      // 响应
      // 响应头:起始行+首部
      // 响应体
    </script>
  </body>
</html>

HTTP方法

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>HTTP 方法</title>
  </head>
  <body>
    <script>
      // 1.常用的 HTTP 方法
      // 浏览器发送请求时采用的方法,和响应无关

      // GET、POST、PUT、DELETE

      // 用来定义对于资源采取什么样的操作的,有各自的语义

      // 2.HTTP 方法的语义
      // GET 获取数据
      // 获取资源(文件)

      // POST 创建数据
      // 注册

      // PUT 更新数据
      // 修改个人信息,修改密码

      // DELETE 删除数据
      // 删除一条评论

      // 增删改查

      // 这些方法虽然有各自的语义,但是并不是强制性的

      // 3.RESTful 接口设计
      // 一种接口设计风格,充分利用 HTTP 方法的语义

      // 通过用户 ID 获取个人信息,使用 GET 方法
      // https://www.imooc.com/api/http/getUser?id=1

      // GET
      // https://www.imooc.com/api/http/user?id=1

      // 注册新用户,使用 POST 方法
      // https://www.imooc.com/api/http/addUser

      // POST
      // https://www.imooc.com/api/http/user

      // 修改一个用户,使用 POST 方法
      // https://www.imooc.com/api/http/modifyUser

      // PUT
      // https://www.imooc.com/api/http/user

      // 删除一个用户,使用 POST 方法
      // https://www.imooc.com/api/http/deleteUser

      // DELETE
      // https://www.imooc.com/api/http/user
    </script>
  </body>
</html>

GET和POST方法

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>GET 和 POST 方法的对比</title>
  </head>
  <body>
    <script>
      // 1.语义
      // GET:获取数据
      // POST:创建数据

      // 2.发送数据
      // GET 通过地址在请求头中携带数据
      // 能携带的数据量和地址的长度有关系,一般最多就几K

      // POST 既可以通过地址在请求头中携带数据,也可以通过请求体携带数据
      // 能携带的数据量理论上是无限的

      // 携带少量数据,可以使用 GET 请求,大量的数据可以使用 POST 请求

      // 3.缓存
      // GET 可以被缓存,POST 不会被缓存

      // 4.安全性
      // ?username=alex
      // GET 和 POST 都不安全

      // 发送密码或其他敏感信息时不要使用 GET,主要是避免直接被他人窥屏或通过历史记录找到你的密码
    </script>
  </body>
</html>

HTTP状态码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>HTTP 状态码</title>
  </head>
  <body>
    <script>
      // 1.HTTP 状态码是什么
      // 定义服务器对请求的处理结果,是服务器返回的

      // 2.HTTP 状态码的语义
      // 100~199 消息:代表请求已被接受,需要继续处理
      // websocket

      // 200~299 成功
      // 200

      // 300~399 重定向
      // http://www.imooc.com/
      // https://www.imooc.com/

      // 301 Moved Permanently
      // 302 Move Temporarily

      // 304 Not Modified

      // 400~499 请求错误
      // 404 Not Found

      // 500~599 服务器错误
      // 500 Internal Server Error
    </script>
  </body>
</html>

本地存储

Cookie

  • 浏览器存储数据的一种方式
  • 存储在用户本地,而不是存储在服务器上
  • 可以随着浏览器每次请求发送到服务器端

初识cookie

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>初识 Cookie</title>
  </head>
  <body>
    <script>
    // 1.Cookie 是什么
    // Cookie 全称 HTTP Cookie,简称 Cookie
    // 是浏览器存储数据的一种方式
    // 因为存储在用户本地,而不是存储在服务器上,是本地存储
    // 一般会自动随着浏览器每次请求发送到服务器端

    // 2.Cookie 有什么用
    // 利用 Cookie 跟踪统计用户访问该网站的习惯,比如什么时间访问,访问了哪些页面,在每个网页的停留时间等

    // 3.在浏览器中操作 Cookie
    // 不要在 Cookie 中保存密码等敏感信息
    </script>
  </body>
</html>

cookie的基本用法

  • 写入cookie
document.cookie = 'username=alex;max-age=5';
document.cookie = 'age=18;domain=.imooc.com;max-age=5';
  • 读取cookie
document.cookie; //username=alex;age=18
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Cookie 的基本用法</title>
  </head>
  <body>
    <script>
      // 1.写入 Cookie
      // document.cookie = 'username=zs';
      // document.cookie = 'age=18';

      // 不能一起设置,只能一个一个设置
      // document.cookie = 'username=zs; age=18';

      // 2.读取 Cookie
      console.log(document.cookie);

      // 读取的是一个由名值对构成的字符串,每个名值对之间由“; ”(一个分号和一个空格)隔开
      // username=zs; age=18
    </script>
  </body>
</html>

Cookie 的属性

  • 名称( Name)和值(Value)
    • 创建 Cookie 时必须填写,其它属性可以使用默认值
    • 如果包含非英文字母,写入时要用 encodeURIComponent() 编码
    • 如果写入时编码,读取时要用 decodeURIComponent() 解码
  • 失效(到期)时间
    • 失效的 Cookie,会被浏览器清除
    • 如果没有设置失效时间,默认会话结束后,Cookie 会被清除
    • 想 Cookie 长时间存在,可以设置 Expires 或 Max-Age
    • Expires 值为 Date 类型,表示具体什么时间过期
    • Max-Age 值为数字,表示当前时间加多少秒后过期,单位是秒
    • 如果设置 Max-Age 的值是 0 或负数,则 Cookie 会被删除
  • Domain
    • Domain 限定了访问 Cookie 的范围(不同域下)
    • 使用 JS 只能写入/读取当前域或父域的 Cookie,无法写入/读取其他域的 Cookie
  • Path
    • Path 限定了访问 Cookie 的范围(同域不同路径下)
    • 使用 JS 只能写入/读取当前路径和上级路径的 Cookie,无法写入/读取其他路径的 Cookie
  • 当 Name、Domain、Path 这 3 个字段都相同的时候,才是同一个 Cookie
  • HttpOnly
    • 前端不能通过 JS 去设置一个 HttpOnly 类型的 Cookie,这种类型的 Cookie 只能是后端来设置
    • 只要是 HttpOnly 类型的,通过 document.cookie 是获取不到的,也不能进行修改
  • Secure
    • Secure 限定了只有在使用了 https 而不是 http 的情况下才可以发送给服务端
  • Domain、Path、Secure 都要满足条件,还不能过期的 Cookie才能随着请求发送到服务器端
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Cookie 的属性</title>
  </head>
  <body>
    <script>
      // 1.Cookie 的名称(Name)和值(Value)
      // 最重要的两个属性,创建 Cookie 时必须填写,其它属性可以使用默认值

      // Cookie 的名称或值如果包含非英文字母,则写入时需要使用 encodeURIComponent() 编码,读取时使用 decodeURIComponent() 解码
      document.cookie = 'username=alex';

      document.cookie = `username=${encodeURIComponent('张三')}`;
      document.cookie = `${encodeURIComponent('用户名')}=${encodeURIComponent(
        '张三'
      )}`;

      // 一般名称使用英文字母,不要用中文,值可以用中文,但是要编码

      // 2.失效(到期)时间
      // 对于失效的 Cookie,会被浏览器清除
      // 如果没有设置失效(到期)时间,这样的 Cookie 称为会话 Cookie
      // 它存在内存中,当会话结束,也就是浏览器关闭时,Cookie 消失

      document.cookie = 'username=alex';

      // 想长时间存在,设置 Expires 或 Max-Age
      // expires
      // 值为 Date 类型
      document.cookie = `username=alex; expires=${new Date(
        '2100-1-01 00:00:00'
      )}`;

      // max-age
      // 值为数字,表示当前时间 + 多少秒后过期,单位是秒
      document.cookie = 'username=alex; max-age=5';
      document.cookie = `username=alex; max-age=${24 * 3600 * 30}`;

      // 如果 max-age 的值是 0 或负数,则 Cookie 会被删除
      document.cookie = 'username=alex';
      document.cookie = 'username=alex; max-age=0';
      document.cookie = 'username=alex; max-age=-1';

      // 3.Domain 域
      // Domain 限定了访问 Cookie 的范围(不同域名)

      // 使用 JS 只能读写当前域或父域的 Cookie,无法读写其他域的 Cookie
      document.cookie = 'username=alex; domain=www.imooc.com';

      // www.imooc.com m.imooc.com 当前域
      // 父域:.imooc.com

      // 4.Path 路径
      // Path 限定了访问 Cookie 的范围(同一个域名下)

      // 使用 JS 只能读写当前路径和上级路径的 Cookie,无法读写下级路径的 Cookie
      document.cookie = 'username=alex; path=/course/list';
      document.cookie = 'username=alex; path=/1.Cookie/';

      // 当 Name、Domain、Path 这 3 个字段都相同的时候,才是同一个 Cookie

      // 5.HttpOnly
      // 设置了 HttpOnly 属性的 Cookie 不能通过 JS 去访问

      // 6.Secure 安全标志
      // Secure 限定了只有在使用了 https 而不是 http 的情况下才可以发送给服务端

      // Domain、Path、Secure 都要满足条件,还不能过期的 Cookie 才能随着请求发送到服务器端
    </script>
  </body>
</html>

cookie的封装

// 写入 Cookie
const set = (name, value, { maxAge, domain, path, secure } = {}) => {
  let cookieText = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;

  if (typeof maxAge === 'number') {
    cookieText += `; max-age=${maxAge}`;
  }

  if (domain) {
    cookieText += `; domain=${domain}`;
  }

  if (path) {
    cookieText += `; path=${path}`;
  }

  if (secure) {
    cookieText += `; secure`;
  }

  document.cookie = cookieText;

  // document.cookie='username=alex; max-age=5; domain='
};

// 通过 name 获取 cookie 的值
const get = name => {
  name = `${encodeURIComponent(name)}`;

  const cookies = document.cookie.split('; ');

  for (const item of cookies) {
    const [cookieName, cookieValue] = item.split('=');

    if (cookieName === name) {
      return decodeURIComponent(cookieValue);
    }
  }

  return;
};

// 'username=alex; age=18; sex=male'
// ["username=alex", "age=18", "sex=male"]
// ["username","alex"]

// get('用户名');

// 根据 name、domain 和 path 删除 Cookie
const remove = (name, { domain, path } = {}) => {
  set(name, '', { domain, path, maxAge: -1 });
};

export { set, get, remove };
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Cookie 的封装</title>
  </head>
  <body>
    <button id="cn">中文</button>
    <button id="en">英文</button>

    <script type="module">
      import { set, get, remove } from './cookie.js';
      import { set, get, remove } from './cookie.js';
      set('username', 'alex');
      set('username', 'zs');
      set('age', 18);
      set('用户名', '张三');

      set('sex', 'male', {
        maxAge: 30 * 24 * 3600
      });

      remove('username');
      remove('用户名');

      console.log(get('username'));
      console.log(get('age'));
      console.log(get('用户名'));
      console.log(get('sex'));

      // 使用封装好的 Cookie 实现网站语言切换
      import { set } from './cookie.js';
      const cnBtn = document.getElementById('cn');
      const enBtn = document.getElementById('en');

      cnBtn.addEventListener(
        'click',
        () => {
          set('language', 'cn', {
            maxAge: 30 * 24 * 3600
          });
          window.location = './2-6.Cookie 的封装.html';
        },
        false
      );
      enBtn.addEventListener(
        'click',
        () => {
          set('language', 'en', {
            maxAge: 30 * 24 * 3600
          });
          window.location = './2-6.Cookie 的封装.html';
        },
        false
      );
    </script>
  </body>
</html>

cookie注意事项

  • 前后端都可以写入和获取 Cookie
  • 每个域名下的 Cookie 数量有限
  • 每个 Cookie 的存储容量很小,最多只有 4KB 左右
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Cookie 的注意事项</title>
  </head>
  <body>
    <script>
      // 1.前后端都可以创建 Cookie

      // 2.Cookie 有数量限制
      // 每个域名下的 Cookie 数量有限

      // 当超过单个域名限制之后,再设置 Cookie,浏览器就会清除以前设置的 Cookie

      // 3.Cookie 有大小限制
      // 每个 Cookie 的存储容量很小,最多只有 4KB 左右
    </script>
  </body>
</html>

localStorage

  • 浏览器存储数据的一种方式
  • 存储在用户本地,不会发送到服务器端
  • 单个域名下的总大小有限制(一般最大 5M左右)
  • setItem()
    • localStorage.setItem(‘username’:‘alex’);
  • getItem()
    • localStorage.getItem(‘username’);
  • removeItem()
    • localStorage.removeItem()
  • clear()
    • localStorage.clear()
  • length
    • localStorage.length

初识 localStorage

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>初识 localStorage</title>
  </head>
  <body>
    <form id="login" action="https://www.imooc.com" method="post">
      <input type="text" name="username" />
      <input type="password" name="password" />
      <input type="submit" id="btn" value="登录" />
    </form>

    <script>
      // 1.localStorage 是什么
      // localStorage 也是一种浏览器存储数据的方式(本地存储),它只是存储在本地,不会发送到服务器端

      // 单个域名下的 localStorage 总大小有限制

      // 2.在浏览器中操作 localStorage

      // 3.localStorage 的基本用法
      console.log(localStorage);

      // // setItem()
      localStorage.setItem('username', 'alex');
      localStorage.setItem('username', 'zs');
      localStorage.setItem('age', 18);
      localStorage.setItem('sex', 'male');

      // // length
      console.log(localStorage.length);

      // // getItem()
      console.log(localStorage.getItem('username'));
      console.log(localStorage.getItem('age'));

      // 获取不存在的返回 null
      console.log(localStorage.getItem('name'));

      // // removeItem()
      localStorage.removeItem('username');
      localStorage.removeItem('age');

      // // // 删除不存在的 key,不报错
      // // localStorage.removeItem('name');

      // // clear()
      localStorage.clear();

      console.log(localStorage);

      // 4.使用 localStorage 实现自动填充
      const loginForm = document.getElementById('login');
      const btn = document.getElementById('btn');

      const username = localStorage.getItem('username');
      if (username) {
        loginForm.username.value = username;
      }

      btn.addEventListener(
        'click',
        e => {
          e.preventDefault();
          // 数据验证

          // console.log(loginForm.username.value);

          localStorage.setItem('username', loginForm.username.value);

          loginForm.submit();
        },
        false
      );
    </script>
  </body>
</html>

localStorage 的注意事项

  • localStorage 存储的数据,除非手动清除,否则永远存在
  • sessionStorage 存储的数据,当会话结束时就会被清除
  • localStorage 存储的键和值只能是字符串类型
  • 不同的域名不能共用 localStorage
  • IE7及以下版本不支持 localStorage,IE8 开始支持
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>localStorage 的注意事项</title>
  </head>
  <body>
    <script>
      // 1.localStorage 的存储期限
      // localStorage 是持久化的本地存储,除非手动清除(比如通过 js 删除,或者清除浏览器缓存),否则数据是永远不会过期的

      // sessionStorage
      // 当会话结束(比如关闭浏览器)的时候,sessionStorage 中的数据会被清空
      sessionStorage.setItem('username', 'alex');
      sessionStorage.getItem('username');
      sessionStorage.removeItem('username');
      sessionStorage.clear();

      // 2.localStorage 键和值的类型
      // localStorage 存储的键和值只能是字符串类型
      // 不是字符串类型,也会先转化成字符串类型再存进去

      localStorage.setItem({}, 18);
      // localStorage.setItem('students', [{},{}]);
      console.log(
        typeof localStorage.getItem('[object Object]'),
        localStorage.getItem('[object Object]')
      );

      // console.log({}.toString()); //[object Object]

      // 3.不同域名下能否共用 localStorage
      // 不同的域名是不能共用 localStorage 的

      // 4.localStorage 的兼容性
      // IE7及以下版本不支持 localStorage,IE8 开始支持

      // caniuse.com
    </script>
  </body>
</html>

Ajax&Fetch跨域请求

Ajax基础

初识Ajax

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>初识 Ajax</title>
  </head>
  <body>
    <script>
      // 1.Ajax 是什么
      // Ajax 是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的简写

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

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

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

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

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

      // ① 慕课网注册检测
      // ② 慕课网搜索提示

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

      // Live Server

      // windows phpStudy
      // Mac MAMP
    </script>
  </body>
</html>

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);

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);

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

初识JSON

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

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('GET', url, true);

xhr.send(null);

// {"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

JSON的三种形式

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

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

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

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

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

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

// [1, "hi", null]

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

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', './plain.json', true); //"str"
// xhr.open('GET', './obj.json', true);
xhr.open('GET', './arr.json', true);

xhr.send(null);

array.json

[
  {
    "id": 1,
    "username": "张三",
    "comment": "666"
  },
  {
    "id": 2,
    "username": "李四",
    "comment": "999 6翻了"
  }
]

obj.json

{
  "name": "张三",
  "age": 18,
  "hobby": ["足球", "乒乓球"],
  "family": {
    "father": "张老大",
    "mother": "李四"
  }
}

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();

storge.js

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.跨域是什么

// 同域,不是跨域
// 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

CORS 跨域资源共享

 // 1.CORS 是什么 由后端负责 前端不用书写
const url = 'https://www.imooc.com';

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('GET', url, true);

xhr.send(null);

// 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/

JSONP跨域资源共享

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

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

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

// 声明函数
const handleResponse = data => {
    console.log(data);
};
<!--手动加载 JSONP 接口-->
<<script>
    // 声明函数
        const handleResponse = data => {
            console.log(data);
        };
</script>
<script src="https://www.imooc.com/api/http/jsonp?callback=handleResponse"></script>
//相当于
<script>
    handleResponse({
        code: 200,
        data: [
          {
            word: 'jsp'
          },
          {
            word: 'js'
          },
          {
            word: 'json'
          },
          {
            word: 'js 入门'
          },
          {
            word: 'jstl'
          }
        ]
      });
</script>

XHR对象

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; //单位ms

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 开始支持

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'
//   })

XHR的事件

// 1.load 事件
// 响应数据可用时触发相当于onreadystatechange
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进阶

FormData

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>FormData</title>
  </head>
  <body>
    <form
      id="login"
      action="https://www.imooc.com/api/http/search/suggest?words=js"
      method="POST"
      enctype="multipart/form-data"
    >
      <input type="text" name="username" placeholder="用户名" />
      <input type="password" name="password" placeholder="密码" />
      <input id="submit" type="submit" value="登录" />
    </form>

    <script>    
        
	  // 1.使用 Ajax 提交表单
        
      // 2.FormData 的基本用法
      // 通过 HTML 表单元素创建 FormData 对象
      // const fd = new FormData(表单元素);
      const login = document.getElementById('login');
      const fd = new FormData(login);
      xhr.send(fd);

      // 3.通过 append() 方法添加数据
      const fd = new FormData(表单元素);
      fd.append('age', 18);
      fd.append('sex', 'male');
      xhr.send(fd);
      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}`;
           xhr.setRequestHeader(
            'Content-Type',
            'application/x-www-form-urlencoded' //相当于Form表单的形式
          );
          xhr.send(data);  // 相当于xhr.send('username=alex&password=12345');
        },
        false
      );
		  // 使用formdata简单化组件的封装
         xhr.addEventListener(
            'load',
            () => {
              if (
                (xhr.status >= 200 && xhr.status < 300) ||
                xhr.status === 304
              ) {
                console.log(xhr.response);
              }
            },
            false
          );

          xhr.open('POST', url, true);
          // 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.send(data);
        },
        false
      );
    </script>
  </body>
</html>

封装Ajax

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>封装 Ajax</title>
  </head>
  <body>
    <script type="module">
      // 常规写法
      // 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.responseText);
      //     }
      //   },
      //   false
      // );
      // xhr.open('GET', url, true);
      // xhr.send(null);
      // 封装Ajax的写法
      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();
    </script>
  </body>
</html>

index.js

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 };

constants.js

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

utils.js

// 工具函数

// 数据序列化成 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 };

defaults.js

// 常量
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;

ajax.js

// 常量
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;

使用 Promise 改造封装好的 Ajax

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>使用 Promise 改造封装好的 Ajax</title>
  </head>
  <body>
    <script type="module">
      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 p = getJSON(url, {
        params: { username: 'alex' },
        data: { age: 18 }
        // timeoutTime: 10
        // success(){},error(){}
      });
      p.xhr.abort();

      const { ERROR_HTTP_CODE, ERROR_REQUEST, ERROR_TIMEOUT, ERROR_ABORT } = p;

      p.then(repsonse => {
        console.log(repsonse);
      }).catch(err => {
        // console.log(err);
        switch (err.type) {
          case ERROR_HTTP_CODE:
            console.log(err.text);
            break;
          case ERROR_REQUEST:
            console.log(err.text);
            break;
          case ERROR_TIMEOUT:
            console.log(err.text);
            break;
          case ERROR_ABORT:
            console.log(err.text);
            break;
        }
      });
    </script>
  </body>
</html>

index.js

import Ajax from './ajax.js';
// 常量
import {
  ERROR_HTTP_CODE,
  ERROR_REQUEST,
  ERROR_TIMEOUT,
  ERROR_ABORT,
  ERROR_HTTP_CODE_TEXT,
  ERROR_REQUEST_TEXT,
  ERROR_TIMEOUT_TEXT,
  ERROR_ABORT_TEXT
} from './constants.js';

const ajax = (url, options) => {
  // return new Ajax(url, options).getXHR();
  let xhr;
  const p = new Promise((resolve, reject) => {
    xhr = new Ajax(url, {
      ...options,
      ...{
        success(response) {
          resolve(response);
        },
        httpCodeError(status) {
          reject({
            type: ERROR_HTTP_CODE,
            text: `${ERROR_HTTP_CODE_TEXT}: ${status}`
          });
        },
        error() {
          reject({
            type: ERROR_REQUEST,
            text: ERROR_REQUEST_TEXT
          });
        },
        abort() {
          reject({
            type: ERROR_ABORT,
            text: ERROR_ABORT_TEXT
          });
        },
        timeout() {
          reject({
            type: ERROR_TIMEOUT,
            text: ERROR_TIMEOUT_TEXT
          });
        }
      }
    }).getXHR();
  });

  p.xhr = xhr;
  p.ERROR_HTTP_CODE = ERROR_HTTP_CODE;
  p.ERROR_REQUEST = ERROR_REQUEST;
  p.ERROR_TIMEOUT = ERROR_TIMEOUT;
  p.ERROR_ABORT = ERROR_ABORT;

  return p;
};

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 };

constants.js

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

export const ERROR_HTTP_CODE = 1;
export const ERROR_HTTP_CODE_TEXT = 'HTTP 状态码异常';
export const ERROR_REQUEST = 2;
export const ERROR_REQUEST_TEXT = '请求被阻止';
export const ERROR_TIMEOUT = 3;
export const ERROR_TIMEOUT_TEXT = '请求超时';
export const ERROR_ABORT = 4;
export const ERROR_ABORT_TEXT = '请求终止';

utils.js、defaults.js、ajax.js都一致

Ajax应用

搜索提示

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>搜索提示</title>
  </head>
  <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>
</html>

二级菜单

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>二级菜单</title>
    <style>
      /* css reset */
      * {
        padding: 0;
        margin: 0;
      }
      li {
        list-style: none;
      }

      /* menu */
      .menu {
        width: 100px;
        background-color: rgba(0, 0, 0, 0.1);
        margin: 10px;
      }
      .menu-item {
        position: relative;
        padding: 5px;
        cursor: pointer;
      }
      .menu-content {
        display: none;
        position: absolute;
        left: 100%;
        top: 0;
        width: 200px;
        height: 100px;
        padding: 0 5px;
        background-color: rgba(0, 0, 0, 0.1);
      }
      .menu-item:hover {
        background-color: rgba(0, 0, 0, 0.4);
      }
      .menu-item:hover .menu-content {
        display: block;
      }
      .menu-loading {
        margin: 45px 0 0 92px;
      }
    </style>
  </head>
  <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>
</html>

多个 Ajax 请求的并发执行

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>多个 Ajax 请求的并发执行</title>
    <style>
      /* css reset */
      * {
        padding: 0;
        margin: 0;
      }
      li {
        list-style: none;
      }

      /* menu */
      .menu {
        width: 100px;
        background-color: rgba(0, 0, 0, 0.1);
        margin: 10px;
      }
      .menu-item {
        position: relative;
        padding: 5px;
        cursor: pointer;
      }
      .menu-content {
        display: none;
        position: absolute;
        left: 100%;
        top: 0;
        width: 200px;
        height: 100px;
        padding: 0 5px;
        background-color: rgba(0, 0, 0, 0.1);
      }
      .menu-item:hover {
        background-color: rgba(0, 0, 0, 0.4);
      }
      .menu-item:hover .menu-content {
        display: block;
      }
      .menu-loading {
        margin: 45px 0 0 92px;
      }

      /* loading-page */
      .loading-page {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 1000;
        background-color: #eee;
        text-align: center;
      }
      .loading-img {
        position: absolute;
        top: 50%;
      }
      .ad img {
        display: inline-block;
        width: 25%;
      }
      .none {
        display: none;
      }
    </style>
  </head>
  <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>
</html>

Ajax扩展

axios

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>axios</title>
  </head>
  <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 库
	  console.log(axios);
      // http://www.axios-js.com/zh-cn/docs/
	  const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      // 2.axios 的基本用法
      axios(url)
        .then(response => {
          console.log(response);
      })
        .catch(err => {
            console.log(err);
        });
      // 引入 axios
      
      
      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  //自动匹配Content-Type
      //   .post(url, 'username=alex&age=18')
      //   .then(response => {
      //     console.log(response);
      //   })
      //   .catch(err => {
      //     console.log(err);
      //   });

      axios //自动匹配Content-Type,无需手动设置
        .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>
</html>

Fetch

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Fetch</title>
  </head>
  <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';
      fetch(url) //使用fetch的默认值,默认使用get方法请求
        .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>
</html>

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Fetch</title>
  </head>
  <body>
    <script>
      // fetch() 调用后返回 Promise 对象
      const url = 'https://www.imooc.com/api/http/search/suggest?words=js';

      // 第二个参数是对象,用来配置fetch,使用POST方法时不能使用请求头携带数据 只能使用请求体
      const fd = new FormData();
      fd.append('username', 'alex');
      fetch(url, {
        method: 'post',
        // body: null
        body: 'username=alex&age=18',
        // body: JSON.stringify({ username: 'alex' }) //不可以直接使用对象,需要使用JSON.stringify()方法来将JS中的值转化为JSON中对应形式的字符串
        // body: fd, //不需要设置Content-Type
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        //   'Content-Type': 'application/json'
        },
        mode: 'cors' //允许跨域 默认值
        // credentials:'include'  //跨域是否携带Cookies
      })
        .then(response => {
          console.log(response);
          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>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值