Skip to content

01 AJAX 入门

知识点自测

  1. 如下对象取值的方式哪个正确?

    js
    let obj = {
      name: '黑马',
    };
    • A: obj.a
    • B: obj()a
    答案

    正确的取值方式是 obj.name。在给定的对象中,属性名是 name,因此应该使用点符号 (.) 来访问该属性的值。

  2. 哪个赋值会让浏览器解析成标签显示?

    js
    let ul = document.querySelector('#ul');
    let str = `<span>我是span标签</span>`;
    • A: ul.innerText = str
    • B: ul.innerHTML = str
    答案
    • 选项 A (ul.innerText = str) 会将字符串 str 的内容直接作为纯文本插入到 ul 元素中,而不会解析为 HTML 标签。
    • 选项 B (ul.innerHTML = str) 将字符串 str 的内容插入到 ul 元素的内部,这将被浏览器解析为 HTML 标签并显示在页面上。 innerHTML 属性允许在元素内插入 HTML 标记。
    • 答案是 B。
  3. 哪个是获取输入框值的方式?

    js
    let theInput = document.querySelector('#input');
    • A: theInput.innerHTML
    • B: theInput.value
    答案
    • 选项 B (theInput.value) 用于获取输入框的值。value 属性用于表示输入框的当前值,而不同于 innerHTML,后者用于获取或设置元素内的 HTML 内容。
  4. 哪个是用于获取标签内容?

    js
    let theP = document.querySelector('#p');
    • A: theP.innerHTML = '内容'
    • B: theP.innerHTML
    答案
    • 选项 B (theP.innerHTML) 表示获取元素 theP 的内部 HTML 内容。这将返回标签内的所有内容,包括嵌套的 HTML 标签。
    • 选项 A (theP.innerHTML = '内容') 是用于设置元素的 HTML 内容,而不是获取。
    • 答案是 B。
  5. 哪个是数组的映射方法?

    • A: arr.forEach
    • B: arr.map
    答案
    • 选项 B (arr.map) 是用于在数组的每个元素上调用一个提供的函数,并返回一个新数组,包含每次函数调用的结果。这允许你对数组的每个元素进行转换、修改或其他操作,并生成一个新的数组。
  6. 数组转字符串并指定拼接符的是哪个?

    • A: arr.join()
    • B: arr.split()
    答案
    • 数组转字符串并指定拼接符的方法是 arr.join()。答案是 A。
    • 选项 A (arr.join()) 是用于将数组的所有元素连接成一个字符串。你可以通过在括号内指定拼接符来定义连接的方式,例如 arr.join(', ') 将数组元素用逗号和空格连接起来。
    • 选项 B (arr.split()) 是用于将字符串分割为数组的方法,并不适用于将数组转换为字符串。
  7. 函数传参的方式哪个是正确的?

    js
    function showAlert(msg, className) {}
    • A:showAlert('消息', '类名')
    • B:showAlert()
    答案
    • 选项 A (showAlert('消息', '类名')) 是正确的,因为函数 showAlert 定义了两个参数 msgclassName,在调用函数时,传递相应数量和顺序的参数,即 '消息''类名'
    • 选项 B (showAlert()) 是不正确的,因为函数 showAlert 需要两个参数,而在这个调用中没有提供任何参数。函数调用应该符合函数定义的参数数量和顺序。
  8. 以下哪套代码可以实现对象属性的简写?

    js
    const username = '老李';
    let obj = {
      username: username,
    };
    js
    const user = '老李';
    let obj = {
      username: user,
    };
    答案
    • 简写对象属性的方式是使用对象属性值的变量名作为属性名,不需要重复写属性名。在给定的选项中,选项 B 就是采用了对象属性的简写形式。
  9. 以下代码的值是多少?

    js
    const age = 10;
    const result = age > 18 ? '成年了' : '未成年';
    • A:'成年了'
    • B:'未成年'
    答案
    • 给定的代码使用了条件(三元)运算符,根据条件的真假返回不同的值。在这个例子中,条件是 age > 18,如果条件为真,则结果是 '成年了',否则结果是 '未成年'
    • 由于给定的 age 是 10,不大于 18,所以条件为假,因此结果是 '未成年'
    • 所以,正确的答案是 B:'未成年'
  10. 以下哪个方法可以添加一个额外类名?

    • A:标签对象.classList.add()
    • B:标签对象.classList.contains()
    答案
    • 选项 A (标签对象.classList.add()) 表示向元素的类列表中添加一个或多个类名。这是用于添加额外类名的方法。
    • 选项 B (标签对象.classList.contains()) 则用于检查元素的类列表中是否包含特定的类名。
    • 答案是 A。

学习目标

  1. 掌握 axios 相关参数,从服务器获取并解析展示数据
  2. 掌握接口文档的查看和使用
  3. 掌握在浏览器的 network 面板中查看请求和响应的内容
  4. 了解请求和响应报文的组成部分

AJAX 概念和 axios 使用

学习目标

  • 了解 AJAX 概念并掌握 axios 库基本使用

什么是 AJAX

  • AJAX(Asynchronous JavaScript and XML)异步 JavaScript 和 XML。
  • AJAX 是一种使用 XMLHttpRequest 技术构建更复杂,动态的网页的编程实践。
  • AJAX 允许只更新一个 HTML 页面的部分 DOM,而无须重新加载整个页面。AJAX 还允许异步工作,这意味着当网页的一部分正试图重新加载时,你的代码可以继续运行(相比之下,同步会阻止代码继续运行,直到这部分的网页完成重新加载)。
  • 通过交互式网站和现代 Web 标准,AJAX 正在逐渐被 JavaScript 框架中的函数和官方的 Fetch API 标准取代。

AJAX

axios 使用

  1. 引入 axios.js 文件到自己的网页中

    html
    <!-- 360 -->
    <script
      crossorigin="anonymous"
      integrity="sha512-JWQFV6OCC2o2x8x46YrEeFEQtzoNV++r9im8O8stv91YwHNykzIS2TbvAlFdeH0GVlpnyd79W0ZGmffcRi++Bw=="
      src="https://lib.baomitu.com/axios/1.6.3/axios.min.js"></script>
    
    <!-- cdnjs -->
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.3/axios.min.js"
      integrity="sha512-JWQFV6OCC2o2x8x46YrEeFEQtzoNV++r9im8O8stv91YwHNykzIS2TbvAlFdeH0GVlpnyd79W0ZGmffcRi++Bw=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"></script>
    
    <!-- jsDelivr -->
    <script
      src="https://cdn.jsdelivr.net/npm/axios@1.6.3/dist/axios.min.js"
      integrity="sha256-OgjfiL+Y/3OY7dtKrPY7eizu+Zt+p9dWsAWROQsfCKU="
      crossorigin="anonymous"></script>
    
    <!-- unpkg -->
    <script src="https://unpkg.com/axios@1.6.3/dist/axios.min.js"></script>
  2. 明确 axios 函数的使用语法

    js
    axios({
      url: '目标资源地址',
    }).then((result) => {
      // 对服务器返回的数据做后续处理
    });

    注意

    • 请求的 url 地址,就是标记资源的网址
    • then 方法这里先体验使用,由来后续会讲到

案例 - 获取省份列表数据

案例 - 获取省份列表数据
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>01 axios 使用 - 获取省份列表数据</title>
</head>

<body>
  <p class="province-list"></p>
  <!-- <script src="https://lib.baomitu.com/axios/1.6.3/axios.min.js"></script> -->
  <script src="./js/axios.min.js"></script>
  <script>

    var config = {
      method: 'get',
      // url: 'https://ajax-api.itheima.net/api/province',
      url: 'http://hmajax.itheima.net/api/province',
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0'
      }
    };

    axios(config)
      .then(response => {
        console.log(response.data)
        console.log(JSON.stringify(response.data))
        // console.log(response.data.data.join(' '))
        // document.querySelector('.province-list').innerHTML = response.data.data.join(' ')

        console.log(response.data.list.join(' '))
        document.querySelector('.province-list').innerHTML = response.data.list.join(' ')
      })
  </script>
</body>

</html>

总结

  1. AJAX 有什么用?

    • 浏览器和服务器之间通信,动态数据交互
  2. AJAX 如何学:

    • 先掌握 axios 库使用,再了解 XMLHttpRequest 原理
  3. 这一节 axios 体验步骤(语法)?

    • 引入 axios 库,使用 axios 相关语法

认识 URL

学习目标

  • 了解 URL 的组成和作用

为什么要认识 URL

虽然是后端给我的一个地址,但是哪部分标记的是服务器电脑,哪部分标记的是资源呢?所以为了和服务器有效沟通我们要认识一下。

什么是 URL

  • 和 Hypertext 以及 HTTP 一样,URL 是 Web 中的一个核心概念。它是浏览器用来检索 web 上公布的任何资源的机制。
  • URL 代表着是统一资源定位符(Uniform Resource Locator)。URL 无非就是一个给定的独特资源在 Web 上的地址。理论上说,每个有效的 URL 都指向一个唯一的资源。这个资源可以是一个 HTML 页面,一个 CSS 文档,一幅图像,等等。而在实际中,也有一些例外,最常见的情况就是一个 URL 指向了不存在的或是被移动过的资源。由于通过 URL 呈现的资源和 URL 本身由 Web 服务器处理,因此 web 服务器的拥有者需要认真地维护资源以及与它关联的 URL。
  • 文档:什么是 URL? - 学习 Web 开发 | MDN
  • 其它

格式

  • 标准格式:[协议类型]://[服务器地址]:[端口号]/[资源层级 UNIX 文件路径][文件名]?[查询]#[片段 ID]
  • 完整格式:[协议类型]://[访问资源需要的凭证信息]@[服务器地址]:[端口号]/[资源层级 UNIX 文件路径][文件名]?[查询]#[片段 ID]
    • 例子:https://developer.mozilla.org/zh-CN/search?q=URL
    • 协议类型:https
    • 域名:
      • 服务器地址:developer.mozilla.org
      • 端口号:80 (默认端口号可以省略)
    • 资源路径:
      • 资源层级 UNIX 文件路径:/zh-CN
      • 文件名:search
    • 参数:
      • 查询:q=URL
      • 片段 ID:#

Tip

你可能想到一个 URL 类似普通信件的地址:

  • 协议代表你要使用的邮政服务
  • 域名是城市或者城镇,端口则像邮政编码;
  • 路径代表着你的信件所有递送的大楼;
  • 参数则提供额外的信息,如大楼所在单元;
  • 锚点表示信件的收件人。

协议

  • http 是协议。它表明了浏览器必须使用何种协议。

  • 它通常都是 HTTP 协议或是 HTTP 协议的安全版,即 HTTPS。Web 需要它们二者之一,但浏览器也知道如何处理其他协议,比如 mailto:(打开邮件客户端)或者 ftp:(处理文件传输),所以当你看到这些协议时,不必惊讶。

  • HTTP 协议类型

    • HTTP (The HyperText Transfer Protocol,超文本传输协议) 是用于在 Web 上传输超媒体文件的底层 协议 ,最典型场景的是在浏览器和服务器之间传递数据,以供人们浏览。现行的 HTTP 标准的版本是 HTTP/2

    • http:// 称为 schema,是 URI 的组成部分,一般位于网络地址的开头。以 https://developer.mozilla.org 为例,该地址说明请求文档时使用 HTTP 协议;这里的 https 代指 HTTP 协议的安全版本,即 SSL (或称 TLS)

    • HTTP 是基于文本的 (所有的通信都以纯文本的形式进行) 以及无状态的 (当前通信状态不会发现以前的通信状态),该特性极大方便了在 www 上浏览网页的人。除此之外,HTTP 也可以用于构建服务器之间交互的 REST web 服务,以及使得网站内容更加动态化的 AJAX 请求。

    • HTTP/2

      • HTTP/2HTTP 网络协议的一个重要版本。HTTP / 2 的主要目标是通过启用完整的请求和响应多路复用来减少 延迟,通过有效压缩 HTTP 标头字段来最小化协议开销,并增加对请求优先级和服务器推送的支持。
      • HTTP/2 不会修改 HTTP 协议的语义。HTTP 1.1 中的所有核心概念(例如 HTTP 方法,状态码,URI 和 headers)都得以保留。而是修改了 HTTP/2 数据在客户端和服务器之间的格式(帧)和传输方式,这两者都管理整个过程,并在新的框架层内隐藏了应用程序的复杂性。所以,所有现有的应用程序都可以不经修改地交付。
    • HTTP/3

  • HTTPS 协议类型

    • HTTPS(超文本传输安全协议)是 HTTP 协议的加密版本。它使用 SSLTLS 协议来加密客户端和服务器之间所有的通信。安全连接允许客户端与服务器安全地交换敏感数据,例如网上银行或者在线商城等涉及金钱的操作。

域名

  • www.example.com 是域名。它表明正在请求哪个 Web 服务器。或者,可以直接使用IP address,但是因为它不太方便,所以它不经常在网络上使用。
  • :80 是端口。它表示用于访问 Web 服务器上的资源的技术“门”。如果 Web 服务器使用 HTTP 协议的标准端口(HTTP 为 80,HTTPS 为 443)来授予其资源的访问权限,则通常会被忽略。否则是强制性的。

资源路径

  • /path/to/myfile.html 是网络服务器上资源的路径。在 Web 的早期阶段,像这样的路径表示 Web 服务器上的物理文件位置。
  • 如今,它主要是由没有任何物理现实的 Web 服务器处理的抽象。

参数

  • ?key1=value1&key2=value2 是提供给网络服务器的额外参数。这些参数是用 & 符号分隔的键/值对列表。
  • 在返回资源之前,Web 服务器可以使用这些参数来执行额外的操作。
  • 每个 Web 服务器都有自己关于参数的规则,唯一可靠的方式来知道特定 Web 服务器是否处理参数是通过询问 Web 服务器所有者。

锚点

  • #SomewhereInTheDocument 是资源本身的另一部分的锚点。
  • 锚点表示资源中的一种“书签”,给浏览器显示位于该“加书签”位置的内容的方向。
  • 例如,在 HTML 文档上,浏览器将滚动到定义锚点的位置;在视频或音频文档上,浏览器将尝试转到锚代表的时间。
  • 值得注意的是,#后面的部分(也称为片段标识符)从来没有发送到请求的服务器。

案例 - 获取新闻列表

案例 - 获取新闻列表
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>02 认识 URL - 获取新闻列表</title>
</head>

<body>
  <div class="news">
    <ul class="news-list"></ul>
  </div>
  <script src="./js/axios.min.js"></script>
  <script>
    var config = {
      method: 'get',
      // url: 'https://ajax-api.itheima.net/api/news',
      url: 'http://hmajax.itheima.net/api/news',
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0'
      }
    };

    axios(config)
      .then(response => {
        console.log(response.data)
        console.log(JSON.stringify(response.data.data))

        console.log(response.data.data.map(item => item.title).join('<br>'))
        document.querySelector('.news-list').innerHTML = response.data.data.map(item => `<li>${item.title}</li>`).join('')
      })
  </script>
</body>

</html>

总结

  1. URL 是什么?

    • 统一资源定位符,网址,用于访问服务器上资源
  2. 请解释这个 URL,每个部分作用?

    bash
    http://hmajax.itheima.net/api/news
    
    协议://域名/资源路径
    - 协议:http
    - 域名:hmajax.itheima.net
    - 资源路径:api/news

URL 查询参数

学习目标

  • 掌握 - 通过 URL 传递查询参数,获取匹配的数据

什么是查询参数

  • 携带给服务器额外信息,让服务器返回我想要的某一部分数据而不是全部数据
  • 举例:查询河北省下属的城市列表,需要先把河北省传递给服务器

9d25cc80-9d1d-44d2-ac70-da59c6ee3cc0

查询参数的语法

  • 在 url 网址后面用?拼接格式:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2
  • 参数名一般是后端规定的,值前端看情况传递即可

axios 如何携带查询参数

  • 使用 params 选项即可
js
axios({
  url: '目标资源地址',
  params: {
    参数名: ,
  },
}).then((result) => {
  // 对服务器返回的数据做后续处理
});

案例 - 获取河北省下属的城市列表

案例 - 获取河北省下属的城市列表
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>03 URL 查询参数 - 获取河北省下属的城市列表</title>
</head>

<body>
  <p class="province-city"></p>
  <script src="./js/axios.min.js"></script>
  <script>
    var config = {
      method: 'get',
      // url: 'https://ajax-api.itheima.net/api/city',
      url: 'http://hmajax.itheima.net/api/city',
      params: {
        pname: '河北省'
      },
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0'
      }
    };

    axios(config)
      .then(response => {
        console.log(response.data)
        console.log(JSON.stringify(response.data))

        console.log(response.data.list.join(' '))
        document.querySelector('.province-city').innerHTML = response.data.list.join(' ')
      })
  </script>
</body>

</html>

总结

  1. URL 查询参数有什么用?

    • 浏览器提供给服务器额外信息,获取对应的数据
  2. axios 要如何携带查询参数?

    • 使用 params 选项,携带参数名和值在对象结构中

案例 - 查询地区列表

案例 - 查询地区列表
  • 需求:根据输入的省份名字和城市名字,查询下属地区列表
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>04 查询地区列表</title>
  <!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha256-MBffSnbbXwHCuZtgPYiwMQbfE7z+GOZ7fBPCNB06Z98=" crossorigin="anonymous"> -->
  <link rel="stylesheet" href="./css/bootstrap.min.css">
</head>

<body class="p-3">
  <div class="container">
    <h2>04 查询地区列表</h2>
    <form class="form-group row" id="editForm">
      <div class="mb-3 col">
        <label for="province" class="form-label">省份</label>
        <select class="form-select" id="province" name="province" onchange="updateCity()">
          <option value="北京" selected>北京</option>
          <option value="甘肃省">甘肃省</option>
          <option value="辽宁省">辽宁省</option>
          <option value="河北省">河北省</option>
          <option value="西藏自治区">西藏自治区</option>
        </select>
      </div>
      <div class="mb-3 col">
        <label for="city" class="form-label">城市</label>
        <select class="form-select" id="city" name="city">
          <option value="北京市" selected>北京市</option>
        </select>
      </div>
    </form>
    <button class="btn btn-primary sel-btn mb-3" type="button">查询</button>
    <div class="mt-3 result">
      <p>地区列表</p>
      <ul class="list-group">
        <li class="list-group-item">东城区</li>
      </ul>
    </div>
  </div>

  <script src="./js/axios.min.js"></script>
  <script>
    function updateCity() {
      var province = document.getElementById("province").value;
      // console.log(province);
      var citySelect = document.getElementById("city");

      // 清空城市下拉菜单
      citySelect.innerHTML = "";

      if (province === "北京") {
        var cities = ["北京市"];
      } else if (province === "甘肃省") {
        var cities = ["兰州市", "嘉峪关市", "金昌市", "白银市", "天水市", "武威市", "张掖市", "平凉市", "酒泉市", "庆阳市", "定西市", "陇南市", "临夏回族自治州", "甘南藏族自治州"];
      } else if (province === "辽宁省") {
        var cities = ["沈阳市", "大连市", "鞍山市", "抚顺市", "本溪市", "丹东市", "锦州市", "营口市", "阜新市", "辽阳市", "盘锦市", "铁岭市", "朝阳市", "葫芦岛市"];
      } else if (province === "河北省") {
        var cities = ["石家庄市", "唐山市", "秦皇岛市", "邯郸市", "邢台市", "保定市", "张家口市", "承德市", "沧州市", "廊坊市", "衡水市"];
      } else if (province === "西藏自治区") {
        var cities = ["拉萨市", "昌都地区", "山南地区", "日喀则地区", "那曲地区", "阿里地区", "林芝地区"];
      } else {
        var cities = [];
      }

      for (var i = 0; i < cities.length; i++) {
        var option = document.createElement("option");
        option.text = cities[i];
        option.value = cities[i];
        citySelect.add(option);
      }
    }
  </script>
  <script>
    document.querySelector(".sel-btn").onclick = function () {
      var province = document.getElementById("province").value;
      var city = document.getElementById("city").value;
      // console.log(province, city);

      var config = {
        method: 'get',
        // url: `http://hmajax.itheima.net/api/area?pname=${province}&cname=${city}`,
        url: `http://hmajax.itheima.net/api/area`,
        params: {
          pname: province,
          cname: city
        },
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0'
        }
      };

      axios(config)
        .then(response => {

          // console.log(response.data)
          console.log(JSON.stringify(response.data))
          // console.log(response.data.data.join(' '))
          // document.querySelector('.province-list').innerHTML = response.data.data.join(' ')

          console.log(response.data.list.join(' '))
          document.querySelector('.list-group').innerHTML = response.data.list.map(item => `<li class="list-group-item">${item}</li>`).join('')
        })
    }
  </script>
</body>

</html>

常用请求方法和数据提交

学习目标

  • 掌握如何向服务器提交数据,而不单单是获取数据
  • 掌握接收 axios 响应错误信息的处理语法

常用请求方法

请求方法操作
GET获取数据
POST提交数据
PUT修改数据(全部)
DELETE删除数据
PATCH修改数据(部分)
  • 请求方法是一些固定单词的英文,例如:GETPOSTPUTDELETEPATCH(这些都是 HTTP 协议规定的),每个单词对应一种对服务器资源要执行的操作。

58336b69-9e80-4757-bfe8-07a39a7a41a1

数据提交

  • 前面我们获取数据其实用的就是 GET 请求方法,但是 axios 内部设置了默认请求方法就是 GET,我们就没有写
  • 但是提交数据需要使用 POST 请求方法

axios 提交数据到服务器

js
axios({
  url: '目标资源地址',
  method: '请求方法',
  data: {
    参数名: ,
  },
}).then((result) => {
  // 对服务器返回的数据做后续处理
});

错误处理

  1. 在 axios 语法中要如何处理呢?

    • 因为,普通用户不会去控制台里看错误信息,我们要编写代码拿到错误并展示给用户在页面上
  2. 使用 axios 的 catch 方法,捕获这次请求响应的错误并做后续处理,语法如下:

    js
    axios({
      // ...请求选项
    })
      .then((response) => {
        // 处理成功数据
      })
      .catch((error) => {
        // 处理失败错误
      });

案例 - 注册账号

案例 - 注册账号
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>05 数据提交 - 注册账号</title>
  <link rel="stylesheet" href="./css/bootstrap.min.css">
</head>

<body class="p-3 mb-2 d-flex justify-content-center align-items-center vh-100">

  <div class="container p-5 shadow" style="width: 500px; background-color: #f5f5f5; border-radius: 10px;">
    <h2>05 注册账号</h2>
    <form class="form-group mt-5">
      <div class="mb-3">
        <label for="exampleInputUsername" class="form-label">Username</label>
        <input type="text" class="form-control" id="username" name="username" placeholder="鸡你太美cxk"
          aria-describedby="usernameHelp">
        <div id="usernameHelp" class="form-text">中英文和数字组成,最少 8 位。</div>
      </div>
      <div class="mb-3">
        <label for="exampleInputPassword1" class="form-label">Password</label>
        <input type="password" class="form-control" id="password" name="password" placeholder="987654321"
          aria-describedby="passwordHelp">
        <div id="passwordHelp" class="form-text">最少 6 位。</div>
      </div>
    </form>
    <button type="submit" class="btn btn-primary mb-3">注册账号</button>
  </div>

  <script src="./js/axios.min.js"></script>
  <script>

    var submitBtn = document.querySelector("button[type=submit]");

    submitBtn.addEventListener("click", () => {
      var username = document.querySelector("#username").value;
      var password = document.querySelector("#password").value;
      console.log(username, password);
      axios.post("http://hmajax.itheima.net/api/register", {
        username,
        password
      }).then(response => {
        console.log(response);
        console.log(response.data.message);
        alert(response.data.message);
      }).catch(error => {
        console.log(error);
        console.log(error.response.data.message);
        alert(error.response.data.message);
      });
    });
  </script>
</body>

</html>

总结

  1. 请求方法最常用的是哪 2 个,分别有什么作用?

    • POST 提交数据
    • GET 查询数据
  2. axios 的核心配置项?

    • url:目标资源地址
    • method:请求方法
    • params:查询参数
    • data:提交的数据
  3. axios 如何拿到请求响应失败的信息?

    • 通过 axios 函数调用后,在后面接着调用 .catch 方法捕获

HTTP 协议 - 请求报文

学习目标

  • 了解 HTTP 协议中,请求报文的组成和作用
  • 了解学习了查看请求报文之后的作用,可以用来辅助错误排查
  • HTTP 协议规定了浏览器和服务器返回内容的格式

请求报文

  • 请求报文:是浏览器按照协议规定发送给服务器的内容,例如刚刚注册用户时,发起的请求报文:

    dd2112e3-4552-4a08-9369-769a7101cffa

    properties
    POST /api/register HTTP/1.1
    Accept: application/json, text/plain, */*
    Accept-Encoding: gzip, deflate
    Accept-Language: zh-CN,zh;q=0.9
    Content-Length: 48
    Content-Type: application/json
    DNT: 1
    Host: hmajax.itheima.net
    Origin: http://127.0.0.1:3000
    Proxy-Connection: keep-alive
    Referer: http://127.0.0.1:3000/
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
    
    {"username":"itheima985","password":"987654321"}
  • 这里的格式包含:

    • 请求行:请求方法,URL,协议
    • 请求头:以键值对的格式携带的附加信息,比如:Content-Type(指定了本次传递的内容类型)
    • 空行:分割请求头,空行之后的是发送给服务器的资源
    • 请求体:发送的资源
  • Chrome 的网络面板如何查看请求体

    2ff38013-27de-43da-b2c7-4433ba5a1073

辅助错误排查

  1. 学习了查看请求报文有什么用呢?
    • 可以用来确认我们代码发送的请求数据是否真的正确
  2. 配套模板代码里,对应 08 标题文件夹里是我同桌的代码,它把登录也写完了,但是无法登录,我们来到模板代码中,找到运行后,在不逐行查看代码的情况下,查看请求报文,看看它登录提交的相关信息对不对,帮他找找问题出现的原因
  3. 发现请求体数据有问题,往代码中定位,找到类名写错误了
  4. 代码:在配套文件夹素材里,找到需要对应代码,直接运行,根据报错信息,找到错误原因

总结

  1. 浏览器发送给服务器的内容叫做
    • 请求报文
  2. 请求报文的组成是什么?
    • 请求行,请求头,空行,请求体
  3. 学会了查看请求报文,对实际开发有什么帮助呢?
    • 可以快速确认我们发送的内容是否正确

HTTP 协议 - 响应报文

学习目标

  • 了解响应报文的组成

响应报文

  • 响应报文:是服务器按照协议固定的格式,返回给浏览器的内容

    9c51781b-3226-4717-b2c2-e05c95ee8da8

    properties
    HTTP/1.1 400 Bad Request
    Content-Length: 54
    Accept-Ranges: bytes
    Access-Control-Allow-Credentials: true
    Access-Control-Allow-Origin: http://127.0.0.1:3000
    Connection: keep-alive
    Content-Type: application/json; charset=utf-8
    Date: Tue, 02 Jan 2024 10:40:27 GMT
    Keep-Alive: timeout=4
    Proxy-Connection: keep-alive
    Server: nginx
    Set-Cookie: acw_tc=2f624a4917041920275134830e32870255ed085870f4c45e154d2a290a6754;path=/;HttpOnly;Max-Age=1800
    Vary: Origin
    X-Content-Type-Options: nosniff
    X-Download-Options: noopen
    X-Frame-Options: SAMEORIGIN
    X-Readtime: 10
    X-Xss-Protection: 1; mode=block
    
    {"code":10005,"message":"账号被占用","data":null}
  • 响应报文的组成:

    • 响应行(状态行):协议,HTTP 响应状态码,状态信息
    • 响应头:以键值对的格式携带的附加信息,比如:Content-Type(告诉浏览器,本次返回的内容类型)
    • 空行:分割响应头,控制之后的是服务器返回的资源
    • 响应体:返回的资源

HTTP 响应状态码

  • 用来表明请求是否成功完成
  • 例如:404(客户端要找的资源,在服务器上不存在)
状态码含义
1XX信息提示类
2XX成功类
3XX重定向类
4XX客户端错误类
5XX服务器端错误类

总结

  1. 响应报文的组成?

    • 响应行,响应头,空行,响应体
  2. HTTP 响应状态码是做什么的?

    • 表明请求是否成功完成,2xx 都是成功的

案例 - 用户登录

案例 - 用户登录
  • Alert 提示框添加/去除 .show 类名即可实现弹框的出现/隐藏。
  • 遇到相同逻辑,重复代码要复用的时候需要封装函数
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>06 用户登录</title>
  <link rel="stylesheet" href="./css/bootstrap.min.css">
  <style>
    .alert {
      transition: .5s;
      opacity: 0;
    }

    .alert.show {
      opacity: 1;
    }
  </style>
</head>

<body class="p-3 mb-2 d-flex justify-content-center align-items-center vh-100">
  <div class="container p-5 shadow-lg bg-body-tertiary rounded-4" style="width: 500px;">
    <h2>06 用户登录</h2>
    <div class="alert alert-success" role="alert">
      提示消息
    </div>
    <form class="form-group" id="editForm">
      <div class="mb-3">
        <label for="username" class="form-label">用户名</label>
        <input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名">
      </div>
      <div class="mb-3">
        <label for="password" class="form-label">密码</label>
        <input type="password" class="form-control" id="password" name="password" placeholder="请输入密码">
      </div>
    </form>
    <button type="botton" class="btn btn-primary btn-login">登录</button>
  </div>

  <script src="./js/axios.min.js"></script>
  <script>
    function showAlert(message, isSuccess) {
      var alert = document.querySelector(".alert");
      alert.classList.add("show");
      alert.innerHTML = message;

      alertStyle = isSuccess ? "alert-success" : "alert-danger";
      alert.classList.add(alertStyle);

      setTimeout(() => {
        alert.classList.remove("show");
        alert.classList.remove(alertStyle);
      }, 2000);
    }

    var loginBtn = document.querySelector(".btn-login");
    loginBtn.addEventListener("click", () => {
      var username = document.querySelector("#username").value;
      var password = document.querySelector("#password").value;
      console.log(username, password);

      if (username.length < 8) {
        console.log("用户名最少 8 位");
        showAlert("用户名最少 8 位", false);
        return;
      }

      if (password.length < 6) {
        console.log("密码最少 6 位");
        showAlert("密码最少 6 位", false);
        return;
      }

      axios.post("http://hmajax.itheima.net/api/login", {
        username,
        password
      }).then(response => {
        console.log(response);
        console.log(response.data.message);
        showAlert(response.data.message, true);
      }).catch(error => {
        console.log(error);
        console.log(error.response.data.message);
        showAlert(error.response.data.message, false);
      });
    });
  </script>

</body>

</html>

form-serialize 插件

  • 使用 form-serialize 插件,快速收集目标表单范围内表单元素的值

  • 语法:

    js
    const form = document.querySelector('form');
    const data = serialize(form, {
      hash: true, // true - 收集出来的是一个 JS 对象结构,false - 收集出来的是一个查询字符串格式
      empty: true, // true - 收集空值,false - 不收集空值
    });
    
    console.log(data);
    const { username, password } = data;

总结

  1. 我们什么时候使用 form-serialize 插件?

    • 快速收集表单元素的值
  2. 如何使用 form-serialize 插件?

    • 先引入插件到自己的网页中,
    • 准备 form 和表单元素的 name 属性,
    • 使用 serialize 函数,传入 form 表单和配置对象
  3. 配置对象中 hash 和 empty 有什么用?

    • hash 决定是收集为 JS 对象还是查询参数字符串,empty 决定是否收集空值

案例 - 用户登录 form-serialize

案例 - 用户登录 form-serialize
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>07 用户登录 form-serialize</title>
  <link rel="stylesheet" href="./css/bootstrap.min.css">
  <style>
    .alert {
      transition: .5s;
      opacity: 0;
    }

    .alert.show {
      opacity: 1;
    }
  </style>
</head>

<body class="p-3 mb-2 d-flex justify-content-center align-items-center vh-100">
  <div class="container p-5 shadow-lg bg-body-tertiary rounded-4" style="width: 500px;">
    <h2>07 用户登录 form-serialize</h2>
    <div class="alert alert-success" role="alert">
      提示消息
    </div>
    <form class="form-group" id="editForm">
      <div class="mb-3">
        <label for="username" class="form-label">用户名</label>
        <input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名">
      </div>
      <div class="mb-3">
        <label for="password" class="form-label">密码</label>
        <input type="password" class="form-control" id="password" name="password" placeholder="请输入密码">
      </div>
    </form>
    <button type="botton" class="btn btn-primary btn-login">登录</button>
  </div>

  <script src="./js/axios.min.js"></script>
  <script src="./js/form-serialize.js"></script>
  <script>
    function showAlert(message, isSuccess) {
      var alert = document.querySelector(".alert");
      alert.classList.add("show");
      alert.innerHTML = message;

      alertStyle = isSuccess ? "alert-success" : "alert-danger";
      alert.classList.add(alertStyle);

      setTimeout(() => {
        alert.classList.remove("show");
        alert.classList.remove(alertStyle);
      }, 2000);
    }

    var loginBtn = document.querySelector(".btn-login");
    loginBtn.addEventListener("click", () => {
      // var username = document.querySelector("#username").value;
      // var password = document.querySelector("#password").value;
      // console.log(username, password);

      const editForm = document.querySelector("#editForm");
      const formData = serialize(editForm, { hash: true, empty: true });
      console.log(formData);
      const { username, password } = formData;
      console.log(username, password);

      if (username.length < 8) {
        console.log("用户名最少 8 位");
        showAlert("用户名最少 8 位", false);
        return;
      }

      if (password.length < 6) {
        console.log("密码最少 6 位");
        showAlert("密码最少 6 位", false);
        return;
      }

      axios.post("http://hmajax.itheima.net/api/login", {
        username,
        password
      }).then(response => {
        console.log(response);
        console.log(response.data.message);
        showAlert(response.data.message, true);
      }).catch(error => {
        console.log(error);
        console.log(error.response.data.message);
        showAlert(error.response.data.message, false);
      });
    });
  </script>

</body>

</html>

今日重点

  1. axios 的配置项有哪几个,作用分别是什么?

    • url: 请求的地址。
    • method: 请求的方法,例如 GETPOST 等。
    • params: 请求的查询参数。
    • data: 请求的请求体数据。
    • headers: 请求的头部信息。
  2. 接口文档都包含哪些信息?

    • 接口地址 (URL): 请求的地址。
    • 请求方法 (Method): 比如 GETPOSTPUT 等。
    • 请求参数 (Parameters): 包括查询参数、路径参数等。
    • 请求体 (Request Body): POST 或者其他有请求体的请求的数据。
    • 请求头 (Request Headers): 请求时需要设置的头部信息。
    • 响应状态码 (Response Status Code): 服务器返回的状态码,如 200404 等。
    • 响应体 (Response Body): 服务器返回的数据。
    • 响应头 (Response Headers): 服务器返回的头部信息。
  3. 在浏览器中如何查看查询参数/请求体,以及响应体数据?

    • 查看查询参数:
      • 打开开发者工具 (F12 或右键点击页面选择"检查")。
      • 切换到 Network 标签。
      • 发起请求后,在列表中选择相应的请求。
      • 在右侧的 HeadersQuery String Parameters 标签下查看查询参数。
    • 查看请求体:
      • Network 标签下选择相应的请求。
      • 切换到 Headers 标签。
      • Request PayloadForm Data 中查看请求体数据。
    • 查看响应体:
      • Network 标签下选择相应的请求。
      • 切换到 Response 标签。
      • PreviewResponse 部分查看响应体数据。
  4. 请求报文和响应报文由几个部分组成,每个部分的作用?

    • 起始行 (Start Line): 包含请求方法、请求目标 (URI) 和协议版本。
    • 头部字段 (Headers): 包含多个键值对,描述请求或响应的属性。
    • 空行 (Blank Line): 用于分隔头部字段和消息体的空行。
    • 消息体 (Message Body): 包含请求或响应的数据,可以是文本、JSON、XML 等格式的数据。

今日作业

客观题

在线答题:Day01_AJAX 入门

  1. 什么是 AJAX(B)?

    • A. 浏览器与浏览器通信数据的方式
    • B. 浏览器与服务器通信的技术
    • C. 本机电脑和其他电脑交互数据的方式
    • D. 就是 axios 库
  2. AJAX 和 axios 关系(D)?

    • A. AJAX 就是 axios
    • B. axios 和 AJAX 没关系
    • C. AJAX 是对 axios 进行了封装
    • D. AJAX 是浏览器通过 XMLHttpRequest 对象与服务器通信,axios 是第三方库对原生 XMLHttpRequest 相关代码进行封装,使用更简单
  3. 以下哪个不是 url 组成部分(C)?

    • A. 协议
    • B. 域名
    • C. 地址
    • D. 资源路径
  4. axios 传参要求用查询参数,选择哪个配置项(B)?

    • A. url
    • B. params
    • C. data
    • D. method
  5. axios 传参要求用请求体,选择哪个配置项(A)?

    • A. data
    • B. params
    • C. url
    • D. method
  6. 以下哪个不是 AJAX 请求的方式(D)?

    • A. GET
    • B. POST
    • C. DELETE
    • D. PUATC
  7. axios 接收错误结果的方法叫(B)?

    • A. then
    • B. catch
    • C. url
    • D. data
  8. 以下哪个是浏览器发给服务器的(A)?

    • A. 请求报文
    • B. 响应报文
    • C. 响应状态码
    • D. 响应体
  9. 服务器返回数据的动作叫(A)?

    • A. 响应
    • B. 请求
    • C. axios
    • D. 请求报文
  10. 以下哪个是 HTTP 响应状态码(B)?

    • A. 49000
    • B. 200
    • C. 10000
    • D. 699

主观题

作业 1 - 微信聊天

  • 目标:完成如下聊天效果

  • 要求:

    • 点击发送和敲击回车键,都能发送聊天消息
    • 把自己和对方消息都展示到页面上
    • 当聊天消息出现滚动条时,始终让最后一条消息出现在视口范围内
  • 接口文档:B 站-AJAX 和黑马头条 - 数据管理平台

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>08 微信聊天</title>
  <link rel="stylesheet" href="./css/font_3736758_vxpb728fcyh.css">
  <link rel="stylesheet" href="./css/reset.min.css">
  <link rel="stylesheet" href="./css/index.css">
</head>

<body>
  <div class="container">
    <!-- 头部 -->
    <div class="top">
      <span>9:41</span>
      <div class="icon">
        <i class="iconfont icon-xinhao"></i>
        <i class="iconfont icon-xinhao1"></i>
        <i class="iconfont icon-electricity-full"></i>
      </div>
    </div>
    <!-- 好友名字 -->
    <div class="friend_name">
      <img src="./assets/arrow-left.png" alt="">
      <span>使劲夸夸</span>
    </div>
    <!-- 聊天区域 -->
    <div class="chat">
      <ul class="chat_list">
        <!-- 他的消息 -->
        <li class="left">
          <img src="./assets/you.png" alt="">
          <span>小宝贝</span>
        </li>
        <!-- 我的消息 -->
        <li class="right">
          <span>干啥</span>
          <img src="./assets/me.png" alt="">
        </li>
      </ul>
    </div>
    <!-- 底部固定 -->
    <div class="bottom_div">
      <!-- 发送消息 -->
      <div class="send_box">
        <div class="input_bg">
          <input class="chat_input" type="text" placeholder="说点什么吧">
        </div>
        <img class="send_img" src="./assets/send.png" alt="">
      </div>
      <!-- 底部黑条 -->
      <div class="black_border">
        <span></span>
      </div>
    </div>
  </div>
  <script src="./js/axios.min.js"></script>
  <script src="./js/index.js"></script>
</body>

</html>
js
// 获取机器人回答消息 - 接口地址:http://hmajax.itheima.net/api/robot
// 查询参数名:spoken
// 查询参数值:我说的消息

// 聊天函数:发送消息,获取机器人回答的消息
let chat = () => {
  // 获取输入框里的消息
  let chatInput = document.querySelector('.chat_input');
  // 获取输入框里的内容,去除首尾空格
  let chatInputValue = chatInput.value.trim();
  console.log(chatInputValue);
  // 判断输入框里的内容是否为空
  if (chatInputValue.length === 0) {
    console.log('输入框里的内容为空!!!');
    return;
  }

  // 获取聊天列表
  let chatList = document.querySelector('.chat_list');
  // 将输入框里的内容添加到聊天列表,然后清空输入框
  chatList.innerHTML += `<li class="right">
      <span>${chatInputValue}</span>
      <img src="./assets/me.png" alt="">
    </li>`;
  chatInput.value = '';

  // 获取聊天区域
  let chatDiv = document.querySelector('.chat');
  // 将聊天区域滚动到底部
  chatDiv.scrollTop = chatDiv.scrollHeight;

  // 发送请求,获取机器人回答的消息
  axios.get('http://hmajax.itheima.net/api/robot', {
    params: {
      spoken: chatInputValue
    }
  }).then(response => {
    // 将机器人回答的消息添加到聊天列表
    // console.log(response);
    console.log(response.data.data.info.text);
    chatList.innerHTML += `<li class="left">
        <img src="./assets/you.png" alt="">
        <span>${response.data.data.info.text}</span>
      </li>`;

    // 将聊天区域滚动到底部
    chatDiv.scrollTop = chatDiv.scrollHeight;
  }).catch(error => {
    console.log(error.message);
  });
}


// 点击发送
document.querySelector('.send_img').addEventListener('click', chat);
// 敲击回车键
document.querySelector('.chat_input').addEventListener('keyup', e => {
  // 判断是否按下回车键
  if (e.keyCode === 13) {
    chat();
  }
})

作业 2 - 必要商城搜索

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <!-- <title>09 必要商城搜索</title> -->
  <title>必要商城_大牌品质 工厂价格</title>
  <link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
  <link rel="stylesheet" href="./css/common.css" />
  <link rel="stylesheet" href="./css/elementUI.css" />
  <link rel="stylesheet" href="./css/global.css" />
  <link rel="stylesheet" href="./css/iprHeader.css" />
  <link rel="stylesheet" href="./css/new.category.css" />
  <link rel="stylesheet" href="./css/new.main.css" />
</head>

<body id="pagebody">
  <div class="header header-index"></div>

  <!-- 导航栏 -->
  <div class="nav nav-index">
    <div class="clearfix">
      <a href="http://www.biyao.com/home/index.html" class="nav-logo"><img src="./assets/logo.png" height="51" /></a>
      <div class="nav-category">
        <p><span>全部分类</span><i></i></p>
        <div>
          <ul class="nav-list">
            <li class="nav-main">
              <p>
                <a href="http://www.biyao.com/classify/category.html?categoryId=621"> 咖啡 </a>
                <span>/</span>
                <a href="http://www.biyao.com/classify/category.html?categoryId=627"> 饮食 </a>
                <span>/</span>
                <a href="http://www.biyao.com/classify/category.html?categoryId=691"> 正餐 </a>
              </p>
            </li>
            <li class="nav-main">
              <p>
                <a href="http://www.biyao.com/classify/category.html?categoryId=279"> 男装 </a>
                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=294"> 女装 </a>
                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=35"> 鞋靴 </a>
              </p>
            </li>
            <li class="nav-main">
              <p>
                <a href="http://www.biyao.com/classify/category.html?categoryId=122"> 眼镜 </a>
                <span>/</span>
                <a href="http://www.biyao.com/classify/category.html?categoryId=339"> 内衣配饰 </a>
                <span>/</span>
                <a href="http://www.biyao.com/classify/category.html?categoryId=39"> 运动 </a>
              </p>
            </li>
            <li class="nav-main">
              <p>
                <a href="http://www.biyao.com/classify/category.html?categoryId=119"> 美妆 </a>
                <span>/</span>
                <a href="http://www.biyao.com/classify/category.html?categoryId=724"> 个护 </a>
                <span>/</span>
                <a href="http://www.biyao.com/classify/category.html?categoryId=391"> 母婴 </a>
              </p>
            </li>

            <li class="nav-main">
              <p>
                <a href="http://www.biyao.com/classify/category.html?categoryId=652"> 生鲜直供 </a>

                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=51"> 餐厨 </a>

                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=334"> 电器 </a>
              </p>
            </li>

            <li class="nav-main">
              <p>
                <a href="http://www.biyao.com/classify/category.html?categoryId=153"> 箱包 </a>

                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=223"> 数码办公 </a>

                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=429"> 汽配 </a>
              </p>
            </li>

            <li class="nav-main">
              <p>
                <a href="http://www.biyao.com/classify/category.html?categoryId=355"> 家纺 </a>

                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=10"> 家具 </a>

                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=369"> 家装 </a>
              </p>
            </li>

            <li class="nav-main">
              <p>
                <a href="http://www.biyao.com/classify/category.html?categoryId=546"> 健康保健 </a>

                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=685"> 宠物 </a>

                <span>/</span>

                <a href="http://www.biyao.com/classify/category.html?categoryId=816"> 礼品 </a>
              </p>
            </li>
          </ul>
        </div>
      </div>
      <div class="nav-search">
        <p><input type="text" id="searchInp" placeholder="请输入要搜索的商品" /><span id="searchButton"></span></p>
        <ul style="">
          <li>行李箱</li>
          <li>洗面奶</li>
          <li>枕头</li>
          <li>袜子男夏季</li>
          <li>防晒霜</li>
          <li>洗发水</li>
          <li>伞</li>
          <li>香水</li>
          <li>眼霜</li>
          <li>精华</li>
        </ul>
      </div>
      <div class="nav-tab">
        <ul>
          <li><a href="http://www.biyao.com/home/index.html">首页</a></li>
          <li><a href="http://www.biyao.com/classify/newProduct.html">每日上新</a></li>
          <li class="border-l"></li>
          <li class="nav-tab-last">
            <div class="hover_text">
              了解必要
              <div class="hover_code gzh">
                <span>关注必要微信公众号<br />了解你想了解的一切<br />小必姐在此发福利哦</span>
              </div>
            </div>
          </li>
          <li class="nav-tab-last" id="appDownload">下载必要 APP</li>
          <li class="border-l"></li>
          <li class="nav-tab-last">
            <div class="hover_text">
              我的必要
              <div class="hover_code app">
                <span>
                  扫码下载必要 app
                  <br />
                  手机用户独享海量权益
                </span>
              </div>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>

  <!-- 右边栏 -->
  <ul class="rightBar" style="display: block">
    <li class="toggle"></li>
    <li class="rightBar-xcx-code toggle novice">
      <div class="coupon_red">
        <div class="tis">
          迎新福利<br />
          微信扫码即得
        </div>
        <div class="rightBar-title">15 元</div>
        <div class="count-down" data-time="7200000" id="count-down"></div>
      </div>
      <div class="rightBar-ercode"></div>
    </li>

    <li class="rightBar-top" style="display: none"></li>
  </ul>

  <!-- 分享弹框 -->
  <div class="shareCon">
    <div>
      <p>分享<b></b></p>
      <div class="share-main">
        <dl>
          <dt><img class="share-code" src="./assets/ewm.jpg" /></dt>
          <dd>扫一扫,分享给好友!</dd>
        </dl>
      </div>
    </div>
  </div>

  <!-- 搜索商品列表 -->
  <ul class="category-container">
    <li>
      <ul class="supplier-recommen category-list clearfix">
        <!-- 每件搜索商品 -->
        <!-- <li>
					<a><i><img
								src="./js/CghkFmIuDbOAUFjRAADsxoYlvAQ019_360x360.jpg"></i>
						<div class="supplier">祖玛珑同原料制造商</div>
						<div class="title">【会呼吸的洗发水】风铃草</div>
						<div class="priceBox">
							<div class="price" price="88"><span
									style="color: #F7A701; font-size: 12px; padding-left: 2px;">¥<span
										style="font-size:18px;">88</span></span></div>
							<div class="mack"><span
									style="color:#FB4C81;background:#FFFFFF; border-color:#FB4C81">一起拼</span><span
									style="color:#FFFFFF;background:#AB7FD1; border-color:#AB7FD1">精选</span></div>
						</div>
						<div class="evaluate">653 条好评</div>
					</a>
				</li> -->
      </ul>
    </li>
  </ul>

  <script type="text/javascript" src="./js/jquery-1.8.3.js"></script>
  <script type="text/javascript" src="./js/jquery.cookie.js"></script>
  <script type="text/javascript" src="./js/md5.js"></script>
  <script type="text/javascript" src="./js/masterCommon.js"></script>
  <script type="text/javascript" src="./js/jquery.extention.js"></script>
  <script type="text/javascript" src="./js/common.js"></script>
  <script type="text/javascript" src="./js/qs.min.js"></script>
  <script type="text/javascript" src="./js/vue.min.js"></script>
  <script type="text/javascript" src="./js/elementUI.min.js"></script>
  <script type="text/javascript" src="./js/utils.js"></script>
  <script src="./js/smcp.min.js"></script>
  <script src="./js/dialog.js"></script>
  <script src="./js/axios.min.js"></script>

  <script src="./js/index.js"></script>
</body>

</html>
js
// 获取搜索框
let searchInp = document.querySelector('#searchInp');

// 搜索函数:根据搜索框的内容,发送请求,获取数据,渲染到页面上
let search = () => {
  // 获取搜索框的内容
  let searchContent = searchInp.value;
  console.log(searchContent);

  // 发送请求
  axios
    .get('https://hmajax.itheima.net/api-s/searchGoodsList', {
      params: {
        searchText: searchContent,
        page: 1,
        everyNum: 16,
      },
    })
    .then((response) => {
      // console.log(response);
      console.log(response.data.list.data);

      //   <ul class="supplier-recommen category-list clearfix">
      // 	<!-- 每件搜索商品 -->
      // 	<!-- <li>
      // 		<a><i><img
      // 					src="./lib/CghkFmIuDbOAUFjRAADsxoYlvAQ019_360x360.jpg"></i>
      // 			<div class="supplier">祖玛珑同原料制造商</div>
      // 			<div class="title">【会呼吸的洗发水】风铃草</div>
      // 			<div class="priceBox">
      // 				<div class="price" price="88"><span
      // 						style="color: #F7A701; font-size: 12px; padding-left: 2px;">¥<span
      // 							style="font-size:18px;">88</span></span></div>
      // 				<div class="mack"><span
      // 						style="color:#FB4C81;background:#FFFFFF; border-color:#FB4C81">一起拼</span><span
      // 						style="color:#FFFFFF;background:#AB7FD1; border-color:#AB7FD1">精选</span></div>
      // 			</div>
      // 			<div class="evaluate">653 条好评</div>
      // 		</a>
      // 	</li> -->
      // </ul>

      // 获取搜索商品列表
      let searchGoodsList = document.querySelector('.supplier-recommen');
      // console.log(searchGoodsList);

      // 判断搜索商品结果是否为空
      if (response.data.list.data.length === 0) {
        console.log('搜索商品结果为空!!!');
      } else {
        // 将数据渲染到页面上
        searchGoodsList.innerHTML = response.data.list.data.map((item) => `<li>
            <a><i><img
                  src="${item.imageUrl}"></i>
              <div class="supplier">祖玛珑同原料制造商</div>
              <div class="title">${item.goodsName}</div>
              <div class="priceBox">
                <div class="price" price="${item.goodsPrice}"><span
                    style="color: #F7A701; font-size: 12px; padding-left: 2px;">¥<span
                      style="font-size:18px;">${item.goodsPrice}</span></span></div>
                <div class="mack"><span
                    style="color:#FB4C81;background:#FFFFFF; border-color:#FB4C81">一起拼</span><span
                    style="color:#FFFFFF;background:#AB7FD1; border-color:#AB7FD1">精选</span></div>
              </div>
              <div class="evaluate">${item.evalNum} 条好评</div>
            </a>
          </li>`).join('');
      }
    }).catch((error) => {
      console.log(error.message);
    });
};

// 搜索按钮添加点击事件
document.querySelector('#searchButton').addEventListener('click', search);

// 搜索框添加敲击回车键事件
searchInp.addEventListener('keyup', (e) => {
  // 判断是否按下回车键
  if (e.keyCode === 13) {
    search();
  }
});

排错题

排错题
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>案例_登录</title>
  <!-- 引入 bootstrap.css -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css">
  <!-- 公共 -->
  <style>
    html,
    body {
      background-color: #EDF0F5;
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .container {
      width: 520px;
      height: 540px;
      background-color: #fff;
      padding: 60px;
      box-sizing: border-box;
    }

    .container h3 {
      font-weight: 900;
    }
  </style>
  <!-- 表单容器和内容 -->
  <style>
    .form_wrap {
      color: #8B929D !important;
    }

    .form-text {
      color: #8B929D !important;
    }
  </style>
  <!-- 提示框样式 -->
  <style>
    .alert {
      transition: .5s;
      opacity: 0;
    }

    .alert.show {
      opacity: 1;
    }
  </style>
</head>

<body>
  <div class="container">
    <h3>欢迎 - 登录</h3>
    <!-- 登录结果 - 提示框 -->
    <div class="alert alert-success" role="alert">
      提示消息
    </div>
    <!-- 表单 -->
    <div class="form_wrap">
      <form>
        <div class="mb-3">
          <label for="username" class="form-label">账号名</label>
          <input type="text" class="form-control username">
        </div>
        <div class="mb-3">
          <label for="password" class="form-label">密码</label>
          <input type="password" class="form-control password">
        </div>
        <button type="button" class="btn btn-primary btn-login"> 登 录 </button>
      </form>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    // 目标:建议使用 network+console 调试来找到问题(共有 6 处错误)
    // 有的问题不会报错,需要自己寻找关键代码来梳理
    // 最后完成登录功能

    // 获取提示框
    const myAlert = document.querySelector('alert')
    function alertFn(msg, isSuccess) {
      myAlert.classList.add('show')

      myAlert.innerText = msg
      const bgStyle = isSuccess ? 'alert-success' : 'alert-danger'
      myAlert.classList.add(bgStyle)

      setTimeout(() => {
        myAlert.classList.remove('show')
        myAlert.classList.remove(bgStyle)
      }, 2000)
    }

    document.querySelector('.btn-login').addEventListener('click', () => {
      const username = document.querySelector('.password').value
      const password = document.querySelector('.password').value

      if (username.length < 8) {
        alertFn('用户名必须大于等于8位', false)
        console.log('用户名必须大于等于8位')
        return
      }
      if (password.length < 6) {
        alertFn('密码必须大于等于6位', false)
        console.log('密码必须大于等于6位')
        return
      }

      axios({
        url: 'http://hmajax.itheima.net/api/lgoin',
        methods: 'POST',
        params: {
          user: username,
          password
        }
      }).then(result => {
        alertFn(result.data.message, true)
        console.log(result)
        console.log(result.data.message)
      }).catch(error => {
        alertFn(error.response.data.message, false)
        console.log(error)
        console.log(error.response.data.message)
      })
    })
  </script>
</body>

</html>
  1. 在使用账号密码登录时,控制台报错。

    bash
    10.排错题.html:89 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'classList')
        at alertFn (10.排错题.html:89:15)
        at 10.排错题.html:128:9

    定位代码片段为

    js
    // 获取提示框
    const myAlert = document.querySelector('alert')
    function alertFn(msg, isSuccess) {
     myAlert.classList.add('show')
     ...
    }

    发现 const myAlert = document.querySelector('alert') 出错, 理应为 const myAlert = document.querySelector('.alert')

  2. 登录提示 GET http://hmajax.itheima.net/api/lgoin?user=987654321&password=987654321 404 (Not Found) 发现请求体数据有问题以及 url 资源路径有问题 定位代码片段为

    js
    const username = document.querySelector('.password').value;
    const password = document.querySelector('.password').value;
    ...
    axios({
      url: 'http://hmajax.itheima.net/api/lgoin',
      methods: 'POST',
      params: {
        user: username,
        password
      }
    • 发现 username 和 password 都是获取的 .password 的值,应该为

      js
      const username = document.querySelector('.username').value;
      const password = document.querySelector('.password').value;
    • 发现 url 链接中资源路径为 api/lgoin,理应为 api/login

    • 发现 methods 写错了,应该为 method

    • 发现应该为 data 传递请求体数据,而不是 params 传递查询参数

    • 发现请求体参数名应该为 usernamepassword,而不是 userpassword

      js
      axios({
        url: 'http://hmajax.itheima.net/api/login',
        method: 'POST',
        data: {
          username,
          password
        }
排错题 (纠正)
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>案例_登录</title>
  <!-- 引入 bootstrap.css -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css">
  <!-- 公共 -->
  <style>
    html,
    body {
      background-color: #EDF0F5;
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .container {
      width: 520px;
      height: 540px;
      background-color: #fff;
      padding: 60px;
      box-sizing: border-box;
    }

    .container h3 {
      font-weight: 900;
    }
  </style>
  <!-- 表单容器和内容 -->
  <style>
    .form_wrap {
      color: #8B929D !important;
    }

    .form-text {
      color: #8B929D !important;
    }
  </style>
  <!-- 提示框样式 -->
  <style>
    .alert {
      transition: .5s;
      opacity: 0;
    }

    .alert.show {
      opacity: 1;
    }
  </style>
</head>

<body>
  <div class="container">
    <h3>欢迎 - 登录</h3>
    <!-- 登录结果 - 提示框 -->
    <div class="alert alert-success" role="alert">
      提示消息
    </div>
    <!-- 表单 -->
    <div class="form_wrap">
      <form>
        <div class="mb-3">
          <label for="username" class="form-label">账号名</label>
          <input type="text" class="form-control username">
        </div>
        <div class="mb-3">
          <label for="password" class="form-label">密码</label>
          <input type="password" class="form-control password">
        </div>
        <button type="button" class="btn btn-primary btn-login"> 登 录 </button>
      </form>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    // 目标:建议使用 network+console 调试来找到问题(共有 6 处错误)
    // 有的问题不会报错,需要自己寻找关键代码来梳理
    // 最后完成登录功能

    // 获取提示框
    const myAlert = document.querySelector('.alert')
    function alertFn(msg, isSuccess) {
      myAlert.classList.add('show')

      myAlert.innerText = msg
      const bgStyle = isSuccess ? 'alert-success' : 'alert-danger'
      myAlert.classList.add(bgStyle)

      setTimeout(() => {
        myAlert.classList.remove('show')
        myAlert.classList.remove(bgStyle)
      }, 2000)
    }

    document.querySelector('.btn-login').addEventListener('click', () => {
      const username = document.querySelector('.username').value
      const password = document.querySelector('.password').value

      if (username.length < 8) {
        alertFn('用户名必须大于等于8位', false)
        console.log('用户名必须大于等于8位')
        return
      }
      if (password.length < 6) {
        alertFn('密码必须大于等于6位', false)
        console.log('密码必须大于等于6位')
        return
      }

      axios({
        url: 'http://hmajax.itheima.net/api/login',
        method: 'POST',
        data: {
          username,
          password
        }
      }).then(result => {
        alertFn(result.data.message, true)
        console.log(result)
        console.log(result.data.message)
      }).catch(error => {
        alertFn(error.response.data.message, false)
        console.log(error)
        console.log(error.response.data.message)
      })
    })
  </script>
</body>

</html>

面试题

  1. GET 和 POST 请求方法的区别?

    • GET 和 POST 请求方式无区别,本质都是一次 HTTP 请求 (TCP 链接), 而 HTTP 请求报文中都包含请求行,请求头,请求体,我们可以在相应位置携带值给服务器

    • 图解:

      211d944c-4d3d-4b9e-82a9-d345b29177c2

    • 但是GET 或 HEAD 请求方式,浏览器会忽略请求体, 但不代表 GET/HEAD 方式无法发送请求体,使用 apifox 等接口调试工具是可以携带请求体的

    • 非要说区别有如下几点

      • 浏览器回退的时候,GET 不会重新提交,而 POST 会重新提交表单
      • GET 会被浏览器主动缓存,POST 不会
      • 再就是 url 上传查询参数和请求体传参的区别了 (实际上不是 GET 和 POST 区别了)
        • url 上只能进行 url 编码,而请求体里支持多种编码格式
        • url 上的参数会保留在浏览器历史记录里,而请求体不会被保留,除非用代码设置
  2. 原生 Ajax 的原理?

    axios 库是对原生 Ajax 的 XMLHttpRequest 相关语法的封装

    AJAX 原理是 XMLHttpRequest 相关语法

    而原生 JS 代码参考 面试官:ajax 原理是什么?如何实现?

  3. 箭头函数和 function 函数区别?

    箭头函数常用做回调函数使用,它无自己的 this, 无 arguments 对象,不能被 new 调用

  4. 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么

    • 浏览器查找域名对应的 IP 地址 (DNS 查询:浏览器缓存->系统缓存->路由器缓存->ISPDNS 缓存->根域名服务器)
    • 浏览器向 Web 服务器发送一个 HTTP 请求(TCP 三次握手)
    • (如果服务器设置了重定向)服务器 301 重定向(从 HTTP://example.com 重定向到 HTTP://www.example.com
    • 浏览器跟踪重定向地址,请求另一个带 www 的网址
    • 服务器处理请求(通过路由读取资源)
    • 服务器返回一个 HTTP 响应(报头中把 Content-type 设置为 'text/html')
    • 浏览器进 DOM 树构建
    • 浏览器发送请求获取嵌在 HTML 中的资源(如图片、音频、视频、CSS、JS 等)
    • 浏览器显示完成页面
    • 浏览器发送异步请求
  5. DOM 事件流与事件委托

    • 事件流

      • 事件流:⼜称为事件传播,是⻚⾯中接收事件的顺序。DOM2 级事件规定的事件流包括了 3 个阶段:

        • 事件捕获阶段(capture phase)
        • 处于⽬标阶段(target phase)
        • 事件冒泡阶段(bubbling phase)
      • 事件捕获(Event Capturing)

        • 事件开始由较为不具体的节点接收后,然后开始逐级向下传播到最具体的元素上。
        • 事件捕获的最大作用在于:事件在到达预定⽬标之前就可以捕获到它。
        • 如果仍以上面那段 HTML 代码为例,当点击按钮后,在事件捕获的过程中,document 对象会首先接收到这个 click 事件,然后再沿着 DOM 树依次向下,直到 <button>。具体顺序如下:
        1. document 对象
        2. html 元素
        3. body 元素
        4. button 元素
      • 事件冒泡(Event Bubbling)

        事件开始由最具体的元素(⽂档中嵌套层次最深的那个节点)接收到后,开始逐级向上传播到较为不具体的节点。

        html
        <html>
          <head>
            <title>Document</title>
          </head>
        
          <body>
            <button>按钮</button>
          </body>
        </html>

        如果点击了上面页面代码中的 <button> 按钮,那么该 click 点击事件会沿着 DOM 树向上逐级传播,在途经的每个节点上都会发生,具体顺序如下:

        1. button 元素
        2. body 元素
        3. html 元素
        4. document 对象
    • 事件委托

      事件委托:利用了事件冒泡的机制,在较上层位置的元素上添加一个事件监听函数,来管理该元素及其所有子孙元素上的某一类的所有事件。

      • 示例

        html
        <ul id="list">
          <li>111</li>
          <li>222</li>
          <li>333</li>
          <li>444</li>
          <li>555</li>
        </ul>
        
        <script type="text/javascript">
          // ⽗元素
          var list = document.getElementById('list');
        
          // 为⽗元素绑定事件,委托管理它的所有⼦元素 li 的点击事件
          list.onclick = function (event) {
            var currentTarget = event.target;
            if (currentTarget.tagName.toLowerCase() === 'li') {
              alert(currentTarget.innerText);
            }
          };
        </script>
      • 适用场景:在绑定大量事件的时候,可以选择事件委托

      • 优点

        • 事件委托可以减少事件注册数量,节省内存占⽤!
        • 当新增⼦元素时,⽆需再次做事件绑定,因此非常适合动态添加元素 (vue 解析模板时,会对新创建的元素,额外进行绑定的)