DayNote(Ajax)

340 阅读8分钟

知识点补充

配置基地址

截屏2021-07-09 下午8.11.43.png

  1. 新建 baseAPi.js
// ajaxPrefilter 方法注册了一个回调函数
// 此回调函数 会在使用 jq 的异步请求方法 前调用
// 并jq的请求的配置对象传给option
// 在回调函数里,就可以拿到 配置对象的url,为其添加上基地址
$.ajaxPrefilter(function(option) {
    option.url = 'http://api-breakingnews-web.itheima.net' + option.url;
})
  1. 在login.html文件中,在login.js文件前引入baseAPi.js
    <script src="/assets/lib/jquery.js"></script>
    <!-- 导入基地址文件 -->
    <script src="/assets/js/baseAPI.js"></script>
    <script src="/assets/js/login.js"></script>
  1. 在login.js文件中,发起的ajax请求,url就可以不写基地址
$.ajax({
            method: 'post',
            url: '/api/login',
            data: data,
            success: function(res) {});
            }
        })

1. 浏览器服务器交互原理

  • IP:在互联网上 电脑的 唯一标识(相当于 门牌号)
  • 端口(Port):是 一台电脑中 为 和网络通信的程序 设置的 "编号ID"(相当于 人名)

1.1 浏览器服务器交互

image-20210310094659520.png

1.2 请求报文和响应报文

image-20210310142342073.png

1.2.1 请求报文

image-20210310094623714.png

1.2.2 响应报文

image-20210310094647332.png

1.3 URL地址

UniformResourceLocator 统一资源定位符

URL组成.png

2. ajax

为什么要用ajax?

  • 用户体验好:因为不需要整个页面都刷新,所以不会影响用户在浏览器的操作
  • 节约资源:因为不需要整个页面刷新,节约了网络流量,减少了页面重绘的面积和次数

2.1 什么是ajax

Ajax 的全称是 Asynchronous JavaScript And XML(异步 JavaScriptxml

通俗理解:在网页中利用 XMLHttpRequest 对象和服务器进行数据交互的方式,就是Ajax

突出的特点:当前网页没有刷新、没有跳转。

2.2 $.get

$.get(url,[data],[callback]) get方式请求,用于获取数据 - 浏览器请求的数据放在url里面 - 服务器响应的的数据 放在响应报文体里 - 浏览器发送请求报文的格式 和 服务器解析报文都是符合http协议的 截屏2021-06-30 上午11.30.57.png

  1. 不带参数的请求 image-20210310105836653.png
  2. 带参数的请求 本质:就是将 get 方法的 第二个参数对象,转成 键值对字符串 添加到 url ? 后面! image-20210310111146030.png

2.3 $.post

$.post(url,[data],[callback]) post方式请求,用于提交数据 - 数据 是 放在 请求报文体 中 传给 服务器的 截屏2021-06-30 下午2.24.48.png

2.3 $.ajax

$.ajax() 比较综合,既可以获取数据,又可以提交数据

$.ajax({
    type: 'GET',
    url: 'http://www.liulongbin.top:3006/api/getbooks',
    data: {
        id: 1
    },
    success: function(res) {
        console.log(res)
    }
})

2.4 接口

使用 Ajax 请求数据时,被请求的 URL 地址,就叫做 数据接口(简称接口)。同时,每个接口必须有请求方式。

服务器端提供的一个方法,我们通过浏览器端去调用服务器的方法

2.5 阻止表单默认提交

// 阻止按钮 默认触发表单默认提交的行为,因为表单提交,整个页面会重新渲染,会造成页面刷新,一般表单的提交会自己再设置,不默认提交
e.preventDefault();

3. Form表单

表单在网页中主要负责 数据采集功能

3.1 form标签的属性

  • action(收件地址)

    • 规定当提交表单时,向何处发送表单数据
    • 属性的值:后端提供的一个URL地址,这个URL地址专门负责接收表单提交过来的数据。
    • 未指定 action 属性值的清空下,action的默认值为当前页面的 URL 地址
    • 当提交表单后,会立即跳转到 action 属性指定的 URL 地址
  • target

    • target 属性用来规定 在何处打开 action URL target属性.png
  • method

    • 规定 以何种方式 把表单数据提交到 action URL
    • get 方式适合用来提交少量的简单的数据
    • post 方式适合用来提交大量的复杂的,或包含文件上传的数据
  • enctype

    • 规定在 发送表单数据之前如何对数据进行编码 enctype属性.png

3.2 表单提交的缺点

缺点:

  • <form> 表单同步提交后,整个页面会发生跳转,跳转到 action URL 所指向的地址,用户体验很差
  • <form> 表单同步提交后,页面之前的状态和数据会丢失

解决方案: 表单只复杂采集数据,Ajax负责将数据提交到服务器

3.3 阻止表单的默认提交

$('#f1').on('submit', function(e) {
    alert('监听到了表单的提交事件2')
    e.preventDefault()
})

3.3 获取表单数据jq.serialize()

**注意:**在使用 serialize() 函数快速获取表单数据时,必须为每个表单元素添加 name 属性

<form action="/login" id="f1">
    <input type="text" name="user_name" />
    <input type="password" name="password" />
    <button type="submit">提交</button>
</form>
   
$('#f1').on('submit', function(e) {
    // 阻止表单的默认提交行为
    e.preventDefault();
    // 获取表单的数据
    let data = $(this).serialize();
    console.log(data); // user_name=用户名值&password密码的值
})

3.4 dom.reset()快速清空表单文本框

注意:reset()是dom对象的方法,所以要将jq对象转换为dom对象 $('#formAddCmt')[0].reset()

     $('#formAddCmt').on('submit', function(e) {
        // 阻止默认提交
        e.preventDefault();
        // 获取表单元素的数据
        let data = $(this).serialize();
        $.post('http://www.liulongbin.top:3006/api/addcmt', data, function(res) {
            if (res.status != 201) return alert('发表评论失败');
            // 更新列表
            upload();
            // 注意this的指向
            // 自动清空文本空内容
            $('#formAddCmt')[0].reset()
        })
    })

4. 模板引擎

模板引擎,它可以根据程序员指定的 模板结构数据,自动生成一个完整的HTML页面

模板引擎.png 好处

  • 减少了字符串的拼接操作
  • 使代码结构更清晰
  • 使代码更易于阅读与维护

4.1 art-template

官网 下载

4.1.1 使用步骤

  1. 导入 art-template

    <script src="./lib/template-web.js"></script>
    
  2. 准备html容器

    <div id="container"></div>
    
  3. 定义数据data

  4. 定义模版

        <script type="text/html" id="tpl-user">
            <h1>{{name}}    ------    {{age}}</h1>
        </script>
    
  5. 调用template函数

    var htmlStr = template('tpl-user', data)
    
  6. 渲染html结构

    $('#container').html(htmlStr)
    

示例

    <!-- 1. 导入模板引擎 -->
    <!-- 在 window 全局,多一个函数,叫做 template('模板的Id', 需要渲染的数据对象) -->
    <script src="./lib/template-web.js"></script>
    <script src="./lib/jquery.js"></script>
</head>

<body>
    <!-- 准备容器 -->
    <div id="container"></div>

    <!-- 3. 定义模板 -->
    <!-- 3.1 模板的 HTML 结构,必须定义到 script 中  -->
    <!-- 注意type要为html,这是一个script标签,而不是当作js去执行 -->
    <script type="text/html" id="tpl-user">
        <h1>{{name}} ------ {{age}}</h1>
        {{@ test}}

        <div>
            {{if flag === 0}} flag的值是0 {{else if flag === 1}} flag的值是1 {{/if}}
        </div>

        <ul>
            {{each hobby}}
            <li>索引是:{{$index}},循环项是:{{$value}}</li>
            {{/each}}
        </ul>

        <h3>{{regTime | dateFormat}}</h3>
    </script>

    <script>
        // 定义处理时间的过滤器
        template.defaults.imports.dateFormat = function(date) {
            var y = date.getFullYear()
            var m = date.getMonth() + 1
            var d = date.getDate()

            return y + '-' + m + '-' + d
        }


        // 2. 定义需要渲染的数据
        var data = {
            name: 'zs',
            age: 20,
            test: '<h3>测试原文输出</h3>',
            flag: 1,
            hobby: ['吃饭', '睡觉', '写代码'],
            regTime: new Date()
        }

        // 4. 调用 template 函数
        var htmlStr = template('tpl-user', data)
        console.log(htmlStr);

        // 5. 渲染HTML结构,放到html容器里面
        $('#container').html(htmlStr)
    </script>
</body>

</html>

4.1.2 标准语法

  1. 原文输出
// 如果包含html标签,保证HTML标签被正常渲染
{{@ value}} 
  1. 条件输出
{{if v1}}
...
{{else if v2}}
...
{{/if}}
  1. 循环输出
{{each arr}}
    <li>索引是:{{$index}},循环项是:{{$value}}</li>
{{/each}}
  1. 过滤器

案例-格式化时间过滤器

  • 定义数据

    var data = { regTime: new Date() }
    
  • 定义过滤器

    // 定义处理时间的过滤器
    template.defaults.imports.dateFormat = function (date) {
          var y = date.getFullYear()
          var m = date.getMonth() + 1
          var d = date.getDate()
    
          return y + '-' + m + '-' + d
    }
    
  • 在模板引擎中使用过滤器

      <script type="text/html" id="tpl-user">
        <h3>{{regTime | dateFormat}}</h3>
      </script>
    

4.2 模版引擎的实现原理

  1. 通过exec()提取出自己想要的字符串
    var str = '<div>我是{{name}}</div>'
    // 正则表达式中 () 包起来的内容表示一个分组,为了提取出"name"
    var pattern = /{{([a-zA-Z]+)}}/

    var result = pattern.exec(str)
    console.log(result)

截屏2021-07-03 上午9.40.09.png

  1. 再通过循环,挨个将提取出的属性名(如name),去data中拿到对应的属性值data[patternResult[1]],并将其str.replace('被替换的值','去替换的值')到标签中
    var data = { name: '张三', age: 20 }

    var str = '<div>{{name}}今年{{ age }}岁了</div>'
    // \s---匹配>=0的空格
    var pattern = /{{\s*([a-zA-Z]+)\s*}}/

    var patternResult = null
    while (patternResult = pattern.exec(str)) {
      str = str.replace(patternResult[0], data[patternResult[1]])
    }
    console.log(str)

4.3 自定义简易的模版引擎

/template.js/

function template(id, data) {
  var str = document.getElementById(id).innerHTML
  var pattern = /{{\s*([a-zA-Z]+)\s*}}/

  var pattResult = null
  while (pattResult = pattern.exec(str)) {
    str = str.replace(pattResult[0], data[pattResult[1]])
  }

  return str
}

/调用自己的模版引擎.html/

  <script src="./js/template.js"></script>
</head>

<body>
  <div id="user-box"></div>

  <script type="text/html" id="tpl-user">
    <div>姓名:{{name}}</div>
    <div>年龄:{{ age }}</div>
    <div>性别:{{  gender}}</div>
    <div>住址:{{address  }}</div>
  </script>

  <script>
    // 定义数据
    var data = { name: 'zs', age: 28, gender: '男', address: '北京顺义马坡' }
    // 调用模板引擎
    var htmlStr = template('tpl-user', data)
    // 渲染HTML结构
    document.getElementById('user-box').innerHTML = htmlStr
  </script>
</body>

5. XMLHttpRequest

XMLHttpRequest(简称 xhr)是浏览器提供的 Javascript 对象,通过它,可以请求服务器上的数据资源

jQuery 中的 Ajax 函数,就是基于 xhr 对象封装出来的

00【重要】xhr.readyState.png

5.1 使用xhr发起GET请求

注意: open和send最好写在最后,避免不必要的bug,且open要写在send之前!!

// 1. 创建 XHR 对象
var xhr = new XMLHttpRequest()

// 4. 监听 onreadystatechange 事件
xhr.onreadystatechange = function () {
    // readyState--监听xh对象的请求状态   status--服务器的响应状态
  if (xhr.readyState === 4 && xhr.status === 200) {
     // 获取服务器响应的数据
     console.log(xhr.responseText)
   }
}

// 2. 调用 open 函数
xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks')
// 3. 调用 send 函数
xhr.send()

1. readyState 属性-表示当前 Ajax 请求所处的状态

readyState属性.png

2. status 属性 - 响应报文中 第一行的 http协议状态码

  • 200 - 服务器运行正常
  • 404 - 服务器 没有 要请求的url
  • 500 - 服务器 报错

3. responseText 属性 - 响应报文体数据(取出来是 文本字符串)

  • 注意:一般 将它 转成 js对象使用 let res = JSON.parse(xhr.responseText)

5.1.1 发起带参数的GET请求

xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks?id=1')

5.1.2 查询字符串

查询字符串(URL 参数)是指在 URL 的末尾加上用于向服务器发送信息的字符串(变量)。

url?id=1&bookname=目送

5.1.3 GET请求携带参数的本质

无论使用 $.ajax(),还是使用 $.get(),又或者直接使用 xhr 对象发起 GET 请求,当需要携带参数的时候, 本质上,都是直接将参数以查询字符串的形式,追加到 URL 地址的后面,发送到服务器的。

get携带数据的本质.png

5.2 URL编码与解码

URL 地址中,只允许出现英文相关的字母、标点符号、数字

所以必须对中文字符进行编码(转义)。

浏览器会自动对 URL 地址进行编码操作

相关知识参考

var str = '黑马程序员';
// 编码 encodeURI(str)
var str2 = encodeURI(str);
console.log(str2); // %E9%BB%91%E9%A9%AC%E7%A8%8B%E5%BA%8F%E5%91%98

console.log('----------');
// 解码 decodeURI(str)
var str3 = decodeURI('%E9%BB%91%E9%A9%AC');
console.log(str3); // 黑马

5.3 使用xhr发起POST请求

// 1. 创建 xhr 对象
var xhr = new XMLHttpRequest()
// 2. 调用 open 函数
xhr.open('POST', 'http://www.liulongbin.top:3006/api/addbook')
// 3. 设置 Content-Type 属性(固定写法)
// 告诉服务器 请求报文体中 的 数据格式 为 键值对格式
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 4. 调用 send 函数,同时指定要发送的数据
xhr.send('bookname=水浒传&author=施耐庵&publisher=上海图书出版社')
// 5. 监听事件
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText)
  }
}

6. 数据交换格式

服务器端客户端之间进行数据传输与交换的格式。(例:XM和JSON)

6.1 XML

EXtensible Markup Language,即可扩展标记语言,和 HTML 类似,都是一种标记语言

  • HTML 被设计用来描述网页上的内容,是网页内容的载体

  • XML 被设计用来传输和存储数据,是数据的载体

    • XML 格式臃肿,和数据无关的代码多,体积大,传输效率低
    • Javascript 中解析 XML 比较麻烦 什么是XML.png

6.2 JSON

JavaScript Object Notation,即“JavaScript 对象表示法

本质:用字符串来表示 Javascript 对象数据或数组数据,是字符串

作用:**在计算机与网络之间存储和传输数据。

  • JSON 是一种轻量级的文本数据交换格式,在作用上类似于 XML,专门用于存储和传输数据
  • XML 更小、更快、更易解析。

6.2.1 语法注意事项

① 属性名必须使用双引号包裹

② 字符串类型的值必须使用双引号包裹

③ 数据类型只能是:数字、字符串、布尔值、null、数组、对象6种类型。

JSON 中不能写注释

JSON 的最外层必须是对象或数组格式

⑥ 不能使用 undefined 或函数作为 JSON 的值

6.2.2 JSON和JS

JSONJS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。

// 对象
let obj={a:'hello' , b:'world'}

// json字符串
let json=' {"a":"hello" , "b":"world"} '
  • JSON 字符串转换为 JS 对象,JSON.parse() ,反序列化
  • JS 字符串转换为 JSON 对象,JSON.stringfy() ,序列化

6.3 封装Ajax函数

/测试页面.html/

<script src="./js/itheima.js"></script>

itheima({
    method: '',
    url: '',
    data: {},
    success:function(res){}
})

/itheima.js/

# 1. 处理data参数,把传入的对象,转为json字符串
/*** 处理 data 参数
* @param {data} 需要发送到服务器的数据
* @returns {string} 返回拼接好的查询字符串 name=zs&age=10
*/
function resolveData(data) {
  var arr = []
  for (var k in data) {
    var str = k + '=' + data[k]
    arr.push(str)
  }
  return arr.join('&');
}

# 2. 定义itheima函数
function itheima(options) {
  var xhr = new XMLHttpRequest();

  // 把外界传递过来的参数对象,转换为 查询字符串
  var qs = resolveData(options.data)
  
  // 判断请求类型,相应地进行send请求操作
  if (options.method.toUpperCase() === 'GET') {
    // 发起GET请求
    xhr.open(options.method, options.url + '?' + qs);
    xhr.send();
  } else if (options.method.toUpperCase() === 'POST') {
    // 发起POST请求
    xhr.open(options.method, options.url);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send(qs);
  }

  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
      var result = JSON.parse(xhr.responseText)
      options.success(result)
    }
  }
}

7. XMLHttpRequest Level2的新特性

  • 旧版XMLHttpRequest的缺点
    • 只支持文本数据的传输,无法用来读取和上传文件

    • 传送和接收数据时,没有进度信息,只能提示有没有完成

  • 新功能
    • 可以设置 HTTP 请求的时限

    • 可以使用 FormData 对象管理表单数据

    • 可以上传文件

    • 可以获得数据传输的进度信息

7.1 设置 HTTP 请求时限 timeout

<script>
  var xhr = new XMLHttpRequest()
  // 设置 超时时间,最长等待时间设为 30 毫秒
  xhr.timeout = 30
  // 设置超时以后的处理函数
  xhr.ontimeout = function () {
    console.log('请求超时了!')
  }
  // 如果请求超时了,就不会再向下执行以下的代码了
  xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks')
  xhr.send()
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
      console.log(xhr.responseText)
    }
  }
</script>

7.2 FormData 对象管理表单数据

HTML5新增了FormData对象

提交表单推荐:

使用form的submit事件,因为form中可能存在多个提交按钮,这样不论点击哪个按钮,都会触发表单的提交事件

  • 使用FormData主要是为了上传文件
  • 只能给post使用
  • 直接获取form表单数据var fd = new FormData(form) , 必须给input表单添加name属性;
  • 如果逐个添加表单数据,var fd = new FormData(); fd.append('uname', 'zs'),此时的 属性名uname 就相当于name 00【重要】异步数据提交方式.png

7.2.1 提交表单数据

 // 1. 新建 FormData 对象
 var fd = new FormData()
 // 2. 为 FormData 添加表单项
 fd.append('uname', 'zs')
 fd.append('upwd', '123456')
 // 3. 创建 XHR 对象
 var xhr = new XMLHttpRequest()
 // 4. 指定请求类型与URL地址
 xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata')
 // 5. 直接提交 FormData 对象,这与提交网页表单的效果,完全一样
 xhr.send(fd)

7.2.2 获取表单数据 new FormData(dom)

  • jq方式:$('#form1').serialize() 可以直接获取form表单中带name属性的值 键值对格式

  • formDate方式:new FormData(form)自动将表单数据填充到 创建formData对象中 formData数据格式

// 获取表单元素
var form = document.querySelector('#form1')
// 监听表单元素的 submit 事件
form.addEventListener('submit', function(e) {
 e.preventDefault()
 // 根据 form 表单创建 FormData 对象,会自动将表单数据填充到 FormData 对象中
 var fd = new FormData(form)
 var xhr = new XMLHttpRequest()
 xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata')
 xhr.send(fd)
 xhr.onreadystatechange = function() {}
})

7.3 上传文件

<body>
    <form id="form1">
        <!-- 1. 文件选择框 -->
        <input type="file" id="file1" name="avatar" />
        <!-- 2. 上传文件的按钮 -->
        <button id="btnUpload">上传文件</button>
    </form>
    <br />
    <!-- 3. img 标签,来显示上传成功以后的图片 -->
    <img src="" alt="" id="img" width="800" />

    <script>
        let btn = document.querySelector('#btnUpload');
        let file1 = document.querySelector('#file1');
        let form1 = document.querySelector('#form1');
        let img = document.querySelector('#img');

        // 1.为表单添加提交事件
        form1.addEventListener('submit', function(e) {
            // 2.阻止表单的默认提交
            e.preventDefault();
            // 3.判断是否上传了图片*****
            if (file1.files.length <= 0) return alert('请上传图片!');
            // 4.通过this获取表单数据*****
            let fd = new FormData(this); // FormData {}

            let xhr = new XMLHttpRequest();
            xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar');
            xhr.send(fd);
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4 && xhr.status == 200) {
                    let data = JSON.parse(xhr.responseText);
                    // data: {message: "上传文件成功!", status: 200, url: "/uploads/1625309578020_f15c0f22d3a143dcae417640c14b4410.jpg"}
                    if (data.status == 200) {
                        // 将服务器返回的图片地址,设置为 <img> 标签的 src 属性
                        // 根路径+data.url*****
                        img.src = 'http://www.liulongbin.top:3006' + data.url;
                    } else {
                        // 上传失败
                        alert(data.message);
                    }
                }
            }
        })
    </script>
</body>

7.4 显示文件上传进度

7.4.1 计算文件上传进度

新版本的 XMLHttpRequest 对象中,可以通过监听 xhr.upload.onprogress 事件,来获取到文件的上传进度。语法格式如下:

// 创建 XHR 对象
var xhr = new XMLHttpRequest()
// 监听 xhr.upload 的 onprogress 事件
xhr.upload.onprogress = function(e) {
     // e.lengthComputable 是一个布尔值,表示当前上传的资源是否具有可计算的长度
     if (e.lengthComputable) {
         // e.loaded 已传输的字节
         // e.total 需传输的总字节
         var percentComplete = Math.ceil((e.loaded / e.total) * 100)
     }
 }

7.4.2 动态设置到进度条上

xhr.upload.onprogress = function(e) {
     if (e.lengthComputable) {
         // 1. 计算出当前上传进度的百分比
         var percentComplete = Math.ceil((e.loaded / e.total) * 100)
         $('#percent')
         // 2. 设置进度条的宽度
         .attr('style', 'width:' + percentComplete + '%')
         // 3. 显示当前的上传进度百分比
         .html(percentComplete + '%')
     }
}

7.4.3 监听上传完成的事件

xhr.upload.onload = function() {
     $('#percent')
     // 移除上传中的类样式
     .removeClass()
     // 添加上传完成的类样式
     .addClass('progress-bar progress-bar-success')
}

7.4.4 完整代码

<body>
    <form id="form1">
        <!-- 1. 文件选择框 -->
        <input type="file" id="file1" name="avatar" />
        <!-- 2. 上传文件的按钮 -->
        <button id="btnUpload">上传文件</button>
    </form>

    <!-- bootstrap 中的进度条 -->
    <div class="progress" style="width: 500px; margin: 15px 10px;">
        <div class="progress-bar progress-bar-striped active" style="width: 0%" id="percent">
            0%
        </div>
    </div>

    <br />
    <!-- 3. img 标签,来显示上传成功以后的图片 -->
    <img src="" alt="" id="img" width="800" />

    <script>
        $('#form1').on('submit', function(e) {
            e.preventDefault();
            // 判断是否上传了文件-------------------------------------------
            // 只有dom对象才有 files属性     $('#file1').get(0)和($('#file1')[0]是一样的
            if ($('#file1')[0].files.length <= 0) return alert('请上传文件!');
            // 创建formdata对象,并将表单数据追加进去
            let fd = new FormData(this);

            // 创建异步对象-----------------
            let xhr = new XMLHttpRequest();

            // 监控文件上传进度-----------------------
            xhr.upload.onprogress = uploadProgress;

            // 监控 请求状态变化---------------------------
            // 通过bind,为函数传入实参 xhr
            xhr.onreadystatechange = stateChange.bind(null, xhr);

            // 最好写在最后面,避免bug
            xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar')
            xhr.send(fd);

        })

        function uploadProgress(e) {
            if (e.lengthComputable) {
                let percent = Math.ceil(e.loaded / e.total * 100) + '%';
                $('#percent').css('width', percent).html(percent);

                // 如果进度到了100%,则修改 进度条样式
                if (percent == '100%') {
                    // 由于 上传完成后,进度条的动画要消耗时间
                    // 所以,0.5s后再将 改变进度条的样式
                    setTimeout(() => {
                        $('#percent').removeClass().addClass('progress-bar progress-bar-success');
                    }, 500);
                }
            }
        }

        function stateChange(xhr) {
            if (xhr.readyState == 4 && xhr.status == 200) {
                let data = JSON.parse(xhr.responseText);
                // 上传成功,为img修改src属性
                if (data.status == 200) {
                    $('#img').prop('src', 'http://www.liulongbin.top:3006' + data.url);
                } else {
                    alert(data.message);
                }
            }
        }
    </script>
</body>

7.5 使用jquery发起上传文件的请求

$.ajax({
     method: 'POST',
     url: 'http://www.liulongbin.top:3006/api/upload/avatar',
     data: fd,
     // 不修改 Content-Type 属性,使用 FormData 默认的 Content-Type 值
     contentType: false,
     // 不对 FormData 中的数据进行 url 编码,而是将 FormData 数据原样发送到服务器
     processData: false,
     success: function(res) {
     	console.log(res)
     }
})

8. axios

jquery提供dom操作 和 ajax操作;而axios只提供ajax操作

为什么要是要使用axios?

vue等框架不需要程序来操作dom,所以不需要使用jq,而是使用只提供ajax操作的axios库(axios更轻量化)

8.1 axios发起GET请求

axios.get('url', { params: { /*参数*/ } }).then(callback)

8.2 axios发起POST请求

axios.post('url', { /*参数*/ }).then(callback)

8.3 直接使用axios发起请求

axios({
 method: '请求类型',
 url: '请求的URL地址',
 data: { /* POST数据 */ },
 params: { /* GET参数 */ }
}).then(callback)

1.发起get请求
document.querySelector('#btn3').addEventListener('click', function () {
      var url = 'http://www.liulongbin.top:3006/api/get'
      var paramsData = { name: '钢铁侠', age: 35 }
      axios({
        method: 'GET',
        url: url,
        params: paramsData
      }).then(function (res) {
        console.log(res.data)
      })
})


2.发起post请求
document.querySelector('#btn4').addEventListener('click', function () {
  axios({
    method: 'POST',
    url: 'http://www.liulongbin.top:3006/api/post',
    data: {
      name: '娃哈哈',
      age: 18,
      gender: '女'
    }
  }).then(function (res) {
    console.log(res.data)
  })
})

截屏2021-07-04 上午11.34.49.png

9. 同源策略

9.1 同源

截屏2021-07-04 下午2.51.08.png

如果两个页面的协议,域名(IP-找服务器)和端口(80-找服务器中的程序)都相同,则两个页面具有相同的源

同源策略.png

同源策略(英文全称 Same origin policy)是浏览器提供的一个安全功能,不允许非同源的 URL 之间进行资源的交互。

9.2 跨域

什么是跨域:

同源指的是两个 URL 的协议、域名、端口一致,反之,则是跨域

跨域检查:

浏览器允许发起ajax跨域请求,但是,跨域请求回来的数据,会被浏览器检查,是否允许ajax跨域请求,如果否,就会拦截报错! 截屏2021-07-04 下午3.20.06.png

浏览器对跨域请求的拦截

浏览器对跨域拦截.png

实现跨域的解决方案:

  1. **JSONP:**出现的早,兼容性好(兼容低版本IE)。是前端程序员为了解决跨域问题,被迫想出来的一种临时解决方案。缺点是只支持 GET 请求,不支持 POST 请求。

  2. **CORS:**出现的较晚,它是 W3C 标准,属于跨域 Ajax 请求的根本解决方案。支持 GETPOST 请求。缺点是不兼容某些低版本的浏览器

  3. 服务器代理:服务器端不直接去发出跨域请求,而在服务器端设置一个代理(proxy),由服务器端向跨域下的代理发出请求,再将请求的结果返回到前端。

9.3 JSONP

<script> 标签不受浏览器同源策略的影响,可以通过 src 属性,请求非同源的 js 脚本。

JSONP 的实现原理: 通过 <script> 标签的 src 属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口响应回来的数据

  • 目的:为了异步获取服务器 接口 返回的数据
  • 问题:浏览器存在 针对ajsx跨域请求 的检查
  • 解决方案:不实用ajax发送异步请求,而是使用script标签来跨域请求,从而绕过浏览器针对ajax的跨域检查

截屏2021-07-04 下午4.17.31.png

/实现一个简单的jsonp/

// 定义一个`success`回调函数:
 <script>
     function success(data) {
     console.log('获取到了data数据:')
     console.log(data)
     }
 </script>

// 通过 `<script>` 标签,请求接口数据:
<script src="http://ajax.frontend.itheima.net:3006/api/jsonp?callback=success&name=zs&a
ge=20"></script>

注意: JSONP 和 Ajax 之间没有任何关系,不能把 JSONP 请求数据的方式叫做 Ajax,因为 JSONP 没有用到XMLHttpRequest 这个对象

9.3.1 jq发送jsonp数据请求

默认情况下,使用 jQuery 发起 JSONP 请求,会自动携带一个 callback=jQueryxxx 的参数,jQueryxxx 是随机生成的一个回调函数名称

$.ajax({
     url: 'http://ajax.frontend.itheima.net:3006/api/jsonp?name=zs&age=20',
     // 如果要使用 $.ajax() 发起 JSONP 请求,必须指定 datatype 为 jsonp
     dataType: 'jsonp',
     // 发送到服务端的参数名称,默认值为 callback,一般不写保持默认值
     jsonp: 'callback',
     // 自定义的回调函数名称,默认值为 jQueryxxx 格式
     jsonpCallback: 'abc',
     success: function(res) {
         console.log(res)
     }
})

jQueryJSONP的实现过程

jQuery 中的 JSONP,也是通过 <script> 标签的 src 属性实现跨域数据访问的,只不过,jQuery 采用的是动态创建和移除标签的方式,来发起 JSONP 数据请求。

  • 在发起 JSONP 请求的时候,动态向 <header> 中 append 一个 <script> 标签;

  • 在 JSONP 请求成功以后,动态从 <header> 中移除刚才 append 进去的 <script> 标签;

9.4 缓存搜索记录

1.定义全局缓存对象
// 缓存对象
var cacheObj = {}
 
2.将搜索结果保存到缓存对象中
// 渲染建议列表
function getList(keyword) {
     // ...省略其他代码
     success(res){
         // 先将 关键字k 和 建议列表数组value,添加到缓存对象中
         cacheObj[keyword] = res;
         
         // 再渲染模版结构
         renderList(res);
     }
}

3.优先从缓存中获取搜索建议
// 监听文本框的 keyup 事件
$('#ipt').on('keyup', function() {
     // ...省略其他代码
     // 在发起请求之前,先判断缓存中是否有数据
     // 如果有,就不用去发送ajax请求,直接将缓存的数据渲染到页面中
     if (cacheObj[keywords]) {
     return renderSuggestList(cacheObj[keywords])
     }
     // 获取搜索建议列表
     debounceSearch(keywords)
 })

9.5 防抖

  • 防抖策略debounce)是当事件被触发后,延迟 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。即:单位事件内计划执行某件事,一旦打断,则重新计时

  • **好处:**能够保证用户在频繁触发某些事件的时候,不会频繁的执行回调,只会被执行一次。

  • 应用场景:

    • 回城,一旦打断就重新计时
    • 用户在输入框中连续输入一串字符时,可以通过防抖策略,只在输入完后,才执行查询的请求,这样可以有效减少请求次数(以最后一次触发开始计时为准),节约请求资源。

防抖.png

var timer = null // 1. 防抖动的 timer
function debounceSearch(keywords) { // 2. 定义防抖的函数
     timer = setTimeout(function() {
     // 发起 JSONP 请求
     getSuggestList(keywords)
     }, 500)
 }
$('#ipt').on('keyup', function() { // 3. 在触发 keyup 事件时,立即清空 timer
 clearTimeout(timer)
 // ...省略其他代码
 debounceSearch(keywords)
 })

9.6 节流

  • 节流策略throttle)单位时间内,不管触发多少次,都只执行一次 节流.png

9.6.1 节流的应用场景

① 鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;

② 懒加载时要监听计算滚动条的位置,但不必每次滑动都触发,可以降低计算的频率,而不必去浪费 CPU 资源;

9.6.2 鼠标跟随效果案例(节流阀)

// 假设 浏览器渲染图片耗时16ms 才能完成
// 所以鼠标没必要 在每次移动时 都重新设置图片的位置
// 因此 可以设置 每隔16ms再 设置一次图片的位置
$(function() {
    // 1. 获取到图片
    var angel = $('#angel');
    // 步骤1. 定义节流阀
    var timer = null;
    // 2. 绑定 mousemove 事件
    $(document).on('mousemove', function(e) {
        // 步骤3:判断节流阀是否为空,如果不为空,则证明距离上次执行间隔不足16毫秒
        if (timer) {
            return
        };
        // 3. 设置图片的位置
        // 步骤2:开启延时器
        timer = setTimeout(function() {
            $(angel).css('top', e.pageY - 40 + 'px').css('left', e.pageX - 40 + 'px')
            console.log('ok');
            // 当设置了鼠标跟随效果后,清空 timer 节流阀,方便下次开启延时器
            timer = null;
        }, 16)
    })
})

10. HTTP

10.1 通信协议

Communication Protocol,指通信的双方完成通信所必须遵守规则和约定

通信双方采用约定好的格式来发送和接收消息,这种事先约定好的通信格式,就叫做通信协议

10.2 HTTP协议

超文本传输协议(HyperText Transfer Protocol),规定了客户端与服务器之间进行网页内容传输时,所必须遵守的传输格式。

10.3 HTTP请求消息

客户端发起的请求叫做 HTTP 请求,客户端发送到服务器的消息,叫做 HTTP 请求消息,又称请求报文。

HTTP请求消息的组成部分

HTTP 请求消息由请求行(request line)、请求头部( header ) 、空行 和 请求体 4 个部分组成。

组成部分.png

10.3.1 请求行

请求行请求方式URLHTTP 协议版本 3 个部分组成,他们之间使用空格隔开。

请求行.png

10.3.2 请求头部

请求头部用来描述客户端的基本信息,从而把客户端相关的信息告知服务器。比如:User-Agent 用来说明当前是什么类型的浏览器;

  • Content-Type 用来描述发送到服务器的数据格式;Accept 用来描述客户端能够接收什么类型的返回内容;
  • Accept-Language 用来描述客户端期望接收哪种人类语言的文本内容。

请求头部由多行 键/值对 组成,每行的键和值之间用英文的冒号分隔 常见请求头.png

常见请求头示例.png 请求头部 – 常见的请求头字段 请求头部.png MDN

10.3.3 空行

最后一个请求头字段的后面是一个空行,通知服务器请求头部至此结束

请求消息中的空行,用来分隔请求头部与请求体

10.3.4 请求体

请求体中存放的,是要通过 POST 方式提交到服务器的数据。

注意:只有 POST 请求才有请求体GET 请求没有请求体

10.4 HTTP响应消息

响应消息就是服务器响应给客户端的消息内容,也叫作响应报文

HTTP响应消息由状态行响应头部空行响应体 4 个部分组成,如下图所示:

相应组成部分.png

10.4.1 状态行

状态行HTTP 协议版本状态码状态码的描述文本 3 个部分组成,他们之间使用空格隔开

相应组成示例.png

10.4.2 响应头部

响应头部用来描述服务器的基本信息。响应头部由多行 键/值对 组成,每行的键和值之间用英文的冒号分隔。

相应头部.png

  • 响应头部 – 常见的响应头字段

相应头部常见字段.png

10.4.3 空行

在最后一个响应头部字段结束之后,会紧跟一个空行,用来通知客户端响应头部至此结束

响应消息中的空行,用来分隔响应头部响应体

10.4.4 响应体

响应体中存放的,是服务器响应给客户端的资源内容。

响应体.png

10.5 HTTP的请求方法

http请求方式.png

10.6 HTTP响应状态码

HTTP Status Code,用来标识响应的状态。MDN官网

HTTP 状态码由三个十进制数字组成第一个十进制数字定义了状态码的类型,后两个数字用来对状态码进行细分

状态码.png

10.6.1 2** 成功相关的响应状态码

表示服务器已成功接收到请求并进行处理

200系列.png

10.6.2 3** 重定向相关的响应状态码

表示服务器要求客户端重定向,需要客户端进一步的操作以完成资源的请求

300系列.png

10.6.3 4** 客户端错误相关的响应状态码

表示客户端的请求有非法内容,从而导致这次请求失败。 400系列.png

10.6.4 5** 服务端错误相关的响应状态码

表示服务器未能正常处理客户端的请求而出现意外错误。 500系列.png