Ajax学习日志

一、客户端与服务器

二、URL地址

2.1 URL概念

统一资源定位符

2.2 URL组成部分

一般由三部分组成

①客户端与服务器之间的通信协议

②存有该资源的服务器名称

③资源在服务器上具体的存放位置

三、分析网页打开过程

客户端发送请求服务器 --> 服务器处理请求 --> 服务器响应客户端

四、服务器对外提供了哪些资源

4.1 数据

  • 网页中的数据也是资源
  • 数据是网页的灵魂
  • 数据也是请求–>处理–>响应

网页中请求服务器上的数据资源,要用到XMLHttpRequest()对象

let xhrObj = new XMLHttpRequest()

4.2 请求方式

get方式(获取)和post(提交)方式

五、了解Ajax

5.1 Ajax

Ajax的全称是Asynchronous Javascript And XMl(异步、 JavaScript 和 XML)

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

5.2 Ajax作用

实现网页与服务器之间的数据交互

5.3 Ajax的典型应用场景

①检测用户名是否被占用

②加载搜索提示列表

③根据页码值动态刷新表格数据

④数据的增删改查

六、jQuery中的Ajax

浏览器中的XMLHttpRequest用法比较复杂,而jQuery对XMlHttpRequest进行封装,提供了一系列Ajax相关函数,极大地降低了Ajax的使用难度

6.1 常用方法

  • $.get() 获取
  • $.post() 提交
  • $.ajax() 既可以获取,也可以提交

6.2 $.get()语法

$.get(url, [data], [callback])
参数名参数类型是否必选说明
urlstring要请求的资源地址
dataobject请求资源期间要携带的参数
callbackfuction请求成功时的回调函数

6.3 $.post()语法

$.post(url, [data], [callback])
参数名参数类型是否必选说明
urlstring要提交的数据地址
dataobject要提交的数据
callbackfuction数据提交成功时的回调函数

6.4 $.ajax()语法

$.ajax({
	type: '',  // 请求方式,例如 GEST 或 POST
	url: '', // 请求的 URL 地址
	datd: {}, // 这次请求要携带的数据
	success: function(res) {}  // 请求成功之后的回调函数
})

七、接口

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

过程:请求 --> 处理 --> 响应

7.1 接口测试工具

不写任何代码情况下,对接口进行调用和测试

PostMan:https://www.postman.com/downloads/

7.2 接口文档

接口的说明文档,它是我们调用接口的依据。

好的接口文档包含了对接口URL,参数以及输出内容的说明,我们参照接口文档就能方便的知道接口的作用,以及接口如何进行调用。

组成部分

接口名称:用来标识各个接口的简单说明,如登录接口、获取信息列表接口等。

接口URL:接口的调用地址。

调用方式:接口的调用方式,如GETPOST

参数格式:接口需要传递的参数,每个参数必须包含参数名称、参数类型、是否必选、参数说明这4项说明。

响应格式:接口返回值的详细描述,一般包括数据名称、数据类型和说明3项内容。

⑥返回示例(可选):通过对象的形式,列举服务器返回数据的结构。

八、案例

8.1 图书管理

目标
通过ajax连接后台,进行后台图书数据的增删,然后将图书数据显示到页面上。

用到的css库 bootstrap.css
用到的javascript库jquery.js
用到的vscode 插件 Bootstrap 3 Snippets

<!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>
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <script src="js/jquery.min.js"></script>
</head>
<body style="padding: 15px;">
    <!-- 添加图书馆的Panel面板 -->
    
    <div class="panel panel-primary">
          <div class="panel-heading">
                <h3 class="panel-title">添加新图书</h3>
          </div>
          <div class="panel-body form-inline">
                
                <div class="input-group">
                    <div class="input-group-addon">书名</div>
                    <input type="text" class="form-control" id="iptBookname" placeholder="请输入书名">
                </div>

                <div class="input-group">
                    <div class="input-group-addon">作者</div>
                    <input type="text" class="form-control" id="iptAuthor" placeholder="请输入作者">
                </div>

                <div class="input-group">
                    <div class="input-group-addon">出版社</div>
                    <input type="text" class="form-control" id="iptPublisher" placeholder="请输入出版社">
                </div>
                
                <button id="btnAdd" class="btn btn-primary">添加</button>
          </div>
    </div>
    
    <!-- 图书表格 -->
    
    <table class="table table-bordered table-hover">
        <thead>
            <tr>
                <th>ID</th>
                <th>书名</th>
                <th>作者</th>
                <th>出版社</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody id="tb">
            <!-- <tr>
                <td></td>
            </tr> -->
        </tbody>
    </table>
    
    <script>
        $(function() {
            function getBooklist() {
                // get请求数据
                $.get('http://www.liulongbin.top:3006/api/getbooks', function(res) {
                    // console.log(res);
                    if (res.status !== 200) return alert('获取数据失败!');

                    let rows = [];
                    // 遍历获得的数据
                    $.each(res.data, function(i, item) {
                        rows.push('<tr><td>' + item.id + '</td><td> ' + item.bookname + ' </td><td> ' + item.author + ' </td><td> ' + item.publisher + ' </td><td><a href="javascript:;" class="del" data-id= ' + item.id + ' ></a></td></tr>')
                    });
                    // 清空tbody,再追加数组rows join('')表示用空字符串拼接
                    $('#tb').empty().append(rows.join(''));
                });
            };

            getBooklist();

            // 事件代理删除操作
            $('#tb').on('click', '.del', function() {
                let id = $(this).data('id');
                $.get('http://www.liulongbin.top:3006/api/delbooks', {id: id}, function(res) {
                    if (res.status !== 200) return alert('删除图书失败!');
                    getBooklist();
                });
            });

            // 添加图书
            $('#btnAdd').on('click', function() {
                // .trim() 去除字符串首尾空格
                let bookname = $('#iptBookname').val().trim();
                let author = $('#iptAuthor').val().trim();
                let publisher = $('#iptPublisher').val().trim();
                if (bookname.length <= 0 || author.length <= 0 || publisher.length <= 0) {
                    return alert('请填写完整的图书信息!');
                } else {
                    // alert('添加成功!');
                    $.post('http://www.liulongbin.top:3006/api/addbook',
                        {bookname: bookname, author: author, publisher: publisher},
                        function(res) {
                            if (res.atatus != 201) return alert('添加图书失败!');
                            getBooklist();
                            $('#iptBookname').val('');
                            $('#iptAuthor').val('');
                            $('#iptPublisher').val('');
                        })
                }
            });
        });
    </script>
</body>
</html>

8.2 聊天机器人

chat.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="css/reset.css" />
    <link rel="stylesheet" href="css/main.css" />
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/jquery-ui.min.js"></script>
    <script type="text/javascript" src="js/jquery.mousewheel.min.js"></script>
    <title>聊天机器人</title>
</head>
<body>
    <div class="wrap">
        <!-- 头部 Header 区域 -->
        <div class="header">
            <h3>小思同学</h3>
            <img src="img/person01.png" alt="icon" />
        </div>
        <!-- 中间 聊天内容区域 -->
        <div class="main">
            <ul class="talk_list" style="top: 0px;" id="talk_list">
                <li class="left_word">
                    <img src="img/person01.png" /> <span>嗨,你最近有想我吗? </span>
                </li>
                <li class="right_word">
                    <img src="img/person02.png"> <span>哈喽,我很想你! </span>
                </li>
                <li class="left_word">
                    <img src="img/person01.png" /> <span>讨厌啦(✿◡‿◡) </span>
                </li>
            </ul>
            <div class="drag_bar" style="display: none;">
                <div class="drager ui-draggable ui-draggable-handle" style="display: none; height: 412.628px;"></div>
            </div>
        </div>
        <!-- 底部 消息编辑区域 -->
        <div class="footer">
            <img src="img/person02.png" alt="icon" />
            <input type="text" placeholder="说些什么吧..." class="input_txt" id="ipt" />
            <input type="button" value="发 送" class="input_sub" id="btnSend" />
        </div>
    </div>
    <!-- 注意 只要为audio指定了新的src属性 而且绑定了 autoplay style="display: none;" 语音会自动播放 并且隐藏起来 -->
    <audio src="" id="voics" autoplay style="display: none;"></audio>

    <!-- 只有插入axios.js文件才可以使用ajax的库 -->
    <script src="js/axios.js"></script>
    <!-- 为了重置滚动条位置 -->
    <script type="text/javascript" src="js/scroll.js"></script>
    <script src="js/chat.js"></script>
</body>
</html>

main.css

body {
    font-family: 'Microsoft YaHei';
}
.wrap {
    position: fixed;
    width: 450px;
    left: 50%;
    margin-left: -225px;
    top: 20px;
    bottom: 20px;
    border: 1px solid #ebebeb;
    background-color: #fff;
    border-radius: 10px;
    box-shadow: 0 0 30px rgba(0, 0, 0, 0.1);
    overflow: hidden;
}
.header {
    height: 55px;
    background: linear-gradient(90deg, rgba(246, 60, 47, 0.6), rgba(128, 58, 242, 0.6));
    overflow: hidden;
}
.header h3 {
    color: #faf3fc;
    line-height: 55px;
    font-weight: normal;
    float: left;
    letter-spacing: 2px;
    margin-left: 25px;
    font-size: 18px;
    text-shadow: 0px 0px 5px #944846;
}
.header img {
    float: right;
    margin: 7px 25px 0 0;
    border-radius: 20px;
    box-shadow: 0 0 5px #f7f2fe;
}
.main {
    position: absolute;
    left: 0;
    right: 0;
    top: 55px;
    bottom: 55px;
    background-color: #f4f3f3;
    box-sizing: border-box;
    padding: 10px 0;
    overflow:hidden;
}
.talk_list{
    position: absolute;
    width:100%;
    left:0px;
    top:0px;
}
.talk_list li {
    overflow: hidden;
    margin: 20px 0px 30px;
}
.talk_list .left_word img {
    float: left;
    margin-left: 20px;
}
.talk_list .left_word span {
    float: left;
    background-color: #fe9697;
    padding: 10px 15px;
    max-width: 290px;
    border-radius: 12px;
    font-size: 16px;
    color: #fff;
    margin-left: 13px;
    position: relative;
    line-height: 24px;
}
.talk_list .left_word span:before {
    content: '';
    position: absolute;
    left: -8px;
    top: 3px;
    width: 13px;
    height: 12px;
    background: url('../img/corner01.png') no-repeat;
}
.talk_list .right_word img {
    float: right;
    margin-right: 20px;
}
.talk_list .right_word span {
    float: right;
    background-color: #fff;
    padding: 10px 15px;
    max-width: 290px;
    border-radius: 12px;
    font-size: 16px;
    color: #000;
    margin-right: 13px;
    position: relative;
    line-height: 24px;
}
.talk_list .right_word span:before {
    content: '';
    position: absolute;
    right: -8px;
    top: 3px;
    width: 13px;
    height: 12px;
    background: url('../img/corner02.png') no-repeat;
}
.drag_bar{
    position:absolute;
    right:0px;
    top:0px;
    background-color: #fff;
    height:100%;
    width:6px;
    box-sizing:border-box;
    border-bottom:1px solid #f4f3f3;
}
.drager{
    position:absolute;
    left:0px;
    top:0px;
    background-color: #cdcdcd;
    height:100px;
    width:6px;
    border-radius:3px;
    cursor: pointer;
}
 
.footer{
    width:100%;
    height: 55px;
    left:0px;
    bottom:0px;
    background-color:#fff;
    position: absolute;
}
 
.footer img{
    float: left;
    margin:8px 0 0 20px;
}
 
.input_txt{
    float: left;
    width:270px;
    height:37px;
    border:0px;
    background-color: #f4f3f3;
    margin:9px 0 0 20px;
    border-radius:8px;
    padding:0px;
    outline:none;
    text-indent:15px;
}
.input_sub{
    float: left;
    width:70px;
    height:37px;
    border:0px;
    background-color: #fe9697;
    margin:9px 0 0 15px;
    border-radius:8px;
    padding:0px;
    outline:none;
    color:#fff;
    cursor: pointer;    
}

reset.css

body,ul,h1,h2,h3,h4,h5,h6{
    margin: 0;
    padding: 0;
}
h1,h2,h3,h4,h5,h6{
    font-size:100%;
    font-weight:normal;
}
a{
    text-decoration:none;
}
ul{
    list-style:none;
}
img{
    border:0px;
}
 
/* 清除浮动,解决margin-top塌陷 */
.clearfix:before,.clearfix:after{
    content:'';
    display:table;    
}
.clearfix:after{
    clear:both;
}
.clearfix{
    zoom:1;
}
 
.fl{
    float:left;
}
.fr{
    float:right;
}

chat.js

$(function () {
    // 调用scroll,重置滚动条位置
    resetui();

    /*
        1.点击发送按钮或者按下回车键
            1.1 获取输入框文本
            1.2 非空判断
            1.3 生成自己的聊天框
            1.4 发送ajax请求机器人聊天内容
            1.5 生成机器人的聊天框
    
        2.数据驱动思路 : 把dom增删改查 改成 数组增删改查
            (1)声明一个全局空数组,用于存储聊天内容   let list = []
            (2)将聊天内容添加到数组     list.push({text:聊天内容, isMe:true})
            (3)渲染数组
    
        3.文字转语音思路
            (1)发送ajax请求: 把文字发给服务器
            (2)服务器响应返回音频url
            (3)给页面添加一个<audio>标签, 把服务器返回的音频url设置给 audio标签的src属性
    */

    $('#btnSend').on('click', function () {
        let text = $('#ipt').val().trim();
        if (text.length <= 0) {
            return $('#ipt').val('');
        }
        // 如果输入了聊天内容,就追加到页面上去
        $('#talk_list').append('<li class="right_word"><img src="img/person02.png"> <span>' + $('#ipt').val() + ' </span></li>');
        $('#ipt').val('');
        // 调用scroll,重置滚动条位置
        resetui();
        // 发送请求,获取回应
        getMag($('#ipt').val(''));
    });

    // 获取机器人的回应,并渲染到页面中
    function getMag(text) {
        $.ajax({
            method: 'GET',
            url: 'http://www.liulongbin.top:3006/api/robot',
            data: {
                spoken: text
            },
            success: function (res) {
                // console.log(res);
                if (res.message == 'success') {
                    // 请求成功 接受聊天消息
                    let msg = res.data.info.text;
                    $('#talk_list').append('<li class="left_word"><img src="img/person01.png"> <span>' + msg + ' </span></li>');
                    // 调用scroll,重置滚动条位置
                    resetui();
                    // 调用把文字转成语音的函数
                    playVoice(msg);
                }
            }
        });
    };

    // 自动播放语音
    function playVoice(text) {
        $.ajax({
            method: 'GET',
            url: 'http://www.liulongbin.top:3006/api/synthesize',
            data: {
                text: text
            },
            success: function (res) {
                if (res.status === 200) {
                    // 设置 audio 标签的src属性
                    $('#voice').attr('src', res.voiceUrl);
                }
            }
        });
    };

    // 优化 - 在输入框里按回车,也能够发送(让按回车的效果和点击发送按钮的效果一样)
    $('#ipt').keyup(function (e) {
        // 键盘弹起之后,触发的函数
        // 获取键盘的keyCode值
        // let keyCode = e.keyCode; // jQuery封装的属性,可以获取到键盘的keyCode
        let keyCode = e.which; // jQuery封装的属性,也可以获取到键盘的keyCode
        // console.log(keyCode);
        // 判断,是否按的是回车键
        if (keyCode === 13) {
            // 说明按了回车键
            // 解决方案一:renderMyWord($(this).val().trim());
            // 解决方案二:触发 发送按钮的单击事件
            // $('#btnSend').click(); // 
            $('#btnSend').trigger('click');
        }
    });

})

九、form与模版引擎

9.1 form表单的基本使用

在这里插入图片描述

  • GET方式提交表单,我们输入的值(请求参数)会拼接到url后面。
  • POST方式提交表单,请求参数,不会拼接到url上。

解决表单同步提交的缺点

缺点

①页面会跳转

②页面之前的状态和数据会丢失

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

9.2 通过Ajax提交表单数据

快速获取表单中的数据

$(selector).serialize()   // 一次性获取表单中的所有数据

注意

①结果是键值对的形式

②必须为每一个表单元素添加name属性

9.3 案例-评论列表

<!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>
    <link rel="stylesheet" href="css/bootstrap.min.css" />
    <script src="js/jquery.min.js"></script>
</head>

<body style="padding: 15px;">
    <!-- 评论面板 -->
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">发表评论</h3>
        </div>
        <form class="panel-body" id="formAddCmt">
            <div>评论人:</div>
            <input type="text" class="form-control" name="username" />
            <div>评论内容:</div>
            <textarea class="form-control" name="content"></textarea>

            <button type="submit" class="btn btn-primary">发表评论</button>
        </form>
    </div>

    <!-- 评论列表 -->

    <ul class="list-group">
        <li class="list-group-item">
            <span class="badge" style="background-color: #F0AD4E;">评论时间:</span>
            <span class="badge" style="background-color: #5BC0DE;">评论人:</span>
            Item
        </li>
    </ul>
    <script>
        $(function () {
            function getCommentList() {
                $.ajax({
                    method: 'GET',
                    url: 'http://www.liulongbin.top:3006/api/cmtlist',
                    success: function (res) {
                        if (res.status !== 200) return alert('获取评论失败!')
                        let rows = [];
                        $.each(res.data, function (i, item) {
                            let str = '<li class="list-group-item"><span class="badge" style="background-color: #F0AD4E;">评论时间:' + item.time + '</span><span class="badge" style="background-color: #5BC0DE;">评论人:' + item.username + '</span>' + item.content + '</li>';
                            rows.push(str)
                        });
                        $('#cmt-list').append(rows.join(''));
                    }
                });

            };
            getCommentList();

            $('#formAddCmt').on('submit', function (e) {
                e.preventDefault();
                let data = $(this).serialize();
                // console.log(data);
                $.post('http://www.liulongbin.top:3006/api/addcmt', data, function(res) {
                    if (res.status !== 201) {
                        return alert('发表评论失败!')
                    } 
                    getCommentList();
                    // 原生的重置表单
                    $('#formAddCmt')[0].reset();
                })
            });
        })
    </script>
</body>
</html>

9.4 模版引擎的基本概念

解决字符串拼接的问题、结构清晰、易于维护和阅读

模版引擎,顾名思义,他可以根据程序员指定的模板结构和数据,自动生成一个完整的HTML页面。

模版引擎

9.5 art-template模版引擎

中文官网:http://aui.github.io/art-template/zh-cn/index.html

9.5.1 art-template的标准语法

**{{}}**是art-template的标准语法(变量填充、循环数组)

  • 在{{}}语法中,可以进行变量的输出、对象属性的输出、三元表达式输出、加减乘除等表达式输出

①原文输出

{{@ value}}, 

输出包含HTML标签结构的内容,保证正常渲染

②条件输出

在{{}}中使用 if…else if…/if 的方式,进行按需输出

{{if value}} 按需输出的内容 {{/if}}

{{if v1}} 按需输出的内容 {{else if v2}} {{/if}}

③循环输出

在{{}}内部通过each语法循环数组,当前循环的索引使用 $index 进行访问,当前的循环项使用 $value 进行访问

{{each arr}}
	{{$index}} {{$value}}
{{/each}}

④过滤器

{{value | filterName}}

管道操作符,他的上一个输出座位下一个输入

template.defaults.imports.filterName = function(value){/*return 处理的结果*/}

模版引擎

<!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>
    <script src="js/jquery.min.js"></script>
    <!-- 多一个template函数(需要模版id和数据对象) -->
    <script src="js/template-web.js"></script>
</head>

<body>
    <div class="container"></div>

    <!-- 定义模版 -->
    <script type="text/html" id="tpl-user">
        <h3>{{name}}:{{age}}</h3>
        {{@ test}}
        <div>
            {{if flag === 0}}
                flag的值是0
            {{else if flag !== 0}}
                flag的值不是0
            {{/if}}
        </div>

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

        <div>注册时间:{{regTime | dateFormat}}</div>
    </script>
    <script>
        // 数据
        let data = {
            name: 'ssy',
            age: 23,
            test: '<h2>哈哈哈哈!</h2>',
            flag: 1,
            hobby: ['吃饭', '睡觉', '写代码'],
            regTime: new Date()
        };

        // 过滤器处理函数
        template.defaults.imports.dateFormat = function (date) {
            // console.log(date);
            let y = date.getFullYear();
            let m = date.getMonth();
            let d = date.getDate();

            return y + '-' + m + '-' + d;  // 注意,过滤器最后一定要return一个值
        };

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

        // 渲染HTML结构
        $('.container').html(htmlStr);
    </script>
</body>

</html>
9.5.2 案例-新闻列表
<!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>
    <style>
        .news-item {
            display: flex;
            border: 1px solid #eee;
            width: 700px;
            padding: 10px;
            margin-bottom: 5px;
        }

        .thumb {
            display: block;
            width: 230px;
            height: 140px;
            background-color: #ccc;
            margin-right: 10px;
        }

        .right-box {
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            font-size: 12px;
            flex: 1;
        }

        .title {
            font-size: 20px;
            font-weight: normal;
        }

        .tags span {
            display: block;
            float: left;
            background-color: #F0F0F0;
            line-height: 20px;
            padding: 0 10px;
            border-radius: 10px;
            margin-right: 8px;
        }

        .footer {
            display: flex;
            justify-content: space-between;
        }
    </style>
    <script src="js/jquery.min.js"></script>
    <script src="js/template-web.js"></script>
</head>

<body>
    <div id="news-list">
        <div class="news-item">
            <img class="thumb" src="" alt="" />
            <div class="right-box">
                <h1 class="title">5G商用在即,三大运营商营收持续下降</h1>
                <div class="tags">
                    <span>三大运营商</span>
                    <span>中国移动</span>
                    <span>5G商用</span>
                </div>
                <div class="footer">
                    <div>
                        <span>胡润百富</span>&nbsp;&nbsp;
                        <span>2023-05-22 20:28:38</span>
                    </div>
                    <span>评论数:66</span>
                </div>
            </div>
        </div>
    </div>
    <!-- 定义模板 -->
    <script type="text/html" id="tpl-news">
    <!-- {{data.length}} -->
    {{each data}}
    <div class="news-item">
        <img class="thumb" src="{{'http://www.liulongbin.top:3006' + $value.img}}" alt="" />
        <div class="right-box">
            <h1 class="title">{{$value.title}}</h1>
            <div class="tags">
                <!-- 循环遍历划分好的字符串数组 -->
                {{each $value.tags}}
                <span>{{$value}}</span>
                {{/each}}
                <!-- <span>中国移动</span>
                    <span>5G商用</span> -->
            </div>
            <div class="footer">
                <div>
                    <span>{{$value.source}}</span>&nbsp;&nbsp;
                    <span>{{$value.time | dateFormat}}</span>
                </div>
                <span>评论数:{{$value.cmtcount}}</span>
            </div>
        </div>
    </div>
    {{/each}}
    </script>

    <script>
        $(function () {
            function padZero(n) {
                n < 10 ? '0' + n : n;
            }
            // 定义时间过滤器
            template.defaults.imports.dateFormat = function (dtStr) {
                let dt = new Date(dtStr);

                let y = date.getFullYear();
                let m = padZero(date.getMonth() + 1);
                let d = padZero(date.getDate());

                let hh = padZero(date.getHours());
                let mm = padZero(date.getMinutes());
                let ss = padZero(date.getSeconds());

                return y + '-' + m + '-' + d + '' + hh + ':' + mm + ':' + ss;
            }
            // 获取新闻列表数据
            function getNewsList() {
                $.get('http://www.liulongbin.top:3006/api/news', function (res) {
                    if (res.status != 200) {
                        return alert('获取新闻列表数据失败!');
                    }
                    // 把每一项的 tags 属性,从字符串转化为字符串数组
                    for (let i = 0; i < res.data.length; i++) {
                        res.data[i].tags = res.data[i].tags.split(',');
                    }
                    // console.log(res.data);
                    let htmlStr = template('tpl-news', res);
                    $('#new-list').html(htmlStr);
                });
            };

            getNewsList();
        })
    </script>
</body>

</html>

9.6 模版引擎的实现原理

9.6.1 正则回顾
  • 区分正则方法和字符串方法
    • 正则方法
      • test()
      • exec()
    • 字符串方法
      • match()
      • replace()
      • split()
      • search()
正则方法由正则表达式调用;字符串方法由字符串调用;
  • exec方法
    • 功能:使用正则表达式匹配字符串,返回值中包含匹配的结果
    • 特点:
      • 一次只能匹配一个结果
      • 再次调用将匹配下一个结果
      • 没有更多匹配则返回null
      • 如果正则表达式中有捕获(小括号),则返回值中包括捕获的结果
  • replace方法
    • 功能:字符串替换
    • 特点:
      • 可以使用正则表达式进行匹配搜索
      • 可以使用函数完成高级的替换
  • 正则分组
<!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>
</head>
<body>
    <script>
        // let str = '<div>我是{{name}},今年{{ age }}岁了</div>';
        // let pattern = /{{\s*([a-zA-Z]+)\s*}}/;

        // // 第一次匹配
        // let result = pattern.exec(str);
        // console.log(result);
        // str = str.replace(result[0], result[1]);
        // console.log(str); // 输出 <div>我是name,今年{{ age }}岁了</div>
        
        // // 第二次匹配
        // result = pattern.exec(str);
        // console.log(result);
        // str = str.replace(result[0], result[1]);
        // console.log(str); // 输出 <div>我是name,今年age岁了</div>

        // // 第三次匹配
        // result = pattern.exec(str);
        // console.log(result); // 输出 null
        // document.write(str);


        // 循环简化多次
        let data = {name: '张三', age: 20};
        console.log(data.name);
        let str = '<div>我是{{name}},今年{{ age }}岁了</div>';
        let pattern = /{{\s*([a-zA-Z]+)\s*}}/;

        let patternResult = null;
        while(patternResult = pattern.exec(str)) {  // 最后patternResult结果为null跳出
            str = str.replace(patternResult[0], data[patternResult[1]]); // 不支持 data.patternResult[1]
        }
        console.log(str);
        document.write(str);
    </script>
</body>
</html>
9.6.2 实现简易的模板引擎

步骤

​ ① 定义模版引擎

​ ② 预调用模板引擎

​ ③ 封装template函数

​ ④导入并使用自定义的模板引擎

自定义简易的模版引擎函数

<!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>
    <script src="js/jquery.min.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>
        // 自定义简易的template函数
        function template(id, data) {
            let str = document.getElementById(id).innerHTML
            let pattern = /{{\s*([a-zA-Z]+)\s*}}/

            let patternResult = null
            while (patternResult = pattern.exec(str)) { // 最后patternResult结果为null跳出
                str = str.replace(patternResult[0], data[patternResult[1]])
            }
            return str
        }

        // 定义数据
        let data = { name: 'Tony', age: 28, gender: '男', address: '河南省郑州市' }

        // 调用模版引擎
        let htmlStr = template('tpl-user', data)

        // 渲染
        $('#user-box').html(htmlStr)
    </script>
</body>

</html>

十、Ajax加强

10.1 XMLHttpRequest的基本使用

10.1.1 使用xhr发起GET请求
<!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>XMLHttpRequest</title>
</head>
<body>
    <script>
        // 1.创建 XHR 对象
        let xhr = new XMLHttpRequest();

        //2.调用 open 函数,指定 请求方式 与 URl 地址
        xhr.open('GET', 'https://autumnfish.cn/api/joke/list?num=5')

        // 3.调用 send 函数,发起 Ajax 请求
        xhr.send()

        // 4.监听 onreadystatuschange 事件
        xhr.onreadystatechange = function () {
            // 4.1 监听 xhr 对象的请求状态 readyState ; 与服务器响应的 status状态
            if (xhr.readyState === 4 && xhr.status === 200) {
                // 4.2 打印服务器响应回来的数据
                console.log(xhr.responseText);
            }
        }
    </script>
</body>
</html>
10.1.2 xhr对象的readyStatus属
状态描述
0UNSENTXMLHttpRequest对象已被创建,但尚未被调用
1OPENDopen()方法已经被调用
2HEADERS_RECEIVEDsend()方法已经被调用,响应头也已经被接受
3LOADNG数据接受中,此时response属性中已经包含部分数据
4DONEAjax请求完成,这意味着数据传输已经被彻底完成失败
10.1.3 URL编码

浏览器会自动处理,我们一般不需太留意

// 编码
encodeURI('红&黑');
// "%E7%BA%A2&%E9%BB%91"

encodeURIComponent('红&黑');
// "%E7%BA%A2%26%E9%BB%91"

// 解码
decodeURI('%E7%BA%A2&%E9%BB%91');
// "红&黑"

decodeURIComponent("%E7%BA%A2%26%E9%BB%91");
// "红&黑"

在实际开发中,需要对中文及需要编码的特殊符号进行URL编码与解码:

使用的编码函数

  • encodeURI() — 能够对中文进行编码
  • encodeURIComponent() — 能够对中文和特殊符号进行编码(&=)

使用的解码函数

  • decodeUPI() — 能够对中文进行解码
  • decodeURIComponent() — 能够对中文和特殊符号进行解码(&=)
10.1.4 xhr发起POST方式的请求

和GET方式的两个区别:

  • 必须在open和send之间,加一个请求头
  • 查询字符串的位置变化为send的参数
// 1. 创建 xhr 对象
let xhr = new XMLHttpRequest();

// 2. 调用open,设置请求方式和url
xhr.open('POST', 'http://www.liulongbin.top:3006/api/addbook');
// 3. POST方式,要设置请求头 --- 和GET方式不一样的一处
// 下面一行代码的意思,告诉服务器,前端提交的数据是什么类型的。表示是查询字符串类型
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// 4. 调用send,发送请求
// xhr.send('key=value&key=valuye.......');
xhr.send('bookname=唐诗三百首&author=老汤&publisher=黑马出版社');

// 5. 注册 onreadystatechange 事件
xhr.onreadystatechange = function () {
    if (this.readyState === 4 && this.status === 200) {
        let res = JSON.parse(this.responseText);
        console.log(res);
    }
}

10.2 数据交换格式

服务器端客户端之间进行数据传输与交换的格式,常用XML标记语言,常用来传输数据和存储数据)和JSON(本质是字符串)

数据交换

10.2.1 JSON

写法

  • 一个json文件(数据),最大的括号或者数组框只能有一个

  • 字符串,属性名和字符串必须使用双引号,不能使用单引号

  • 不能写注释

  • json中不能有 undefined

**两种结构:**对象结构(键值对形式)、数组结构

JSON的数据类型:number、string、null、bool、array、object

序列化:把数据对象转化为字符串的过程叫做序列化,例如:调用JSON.stringify()函数操作,叫做JSON序列化

反序列化:把字符串转化为数据对象的过程叫做序列化,例如:调用JSON.parse()函数操作,叫做JSON反序列化

10.3 封装自己的Ajax函数

<script>
    // 对象转化为拼接字符串函数
    function resolveData(data) {
        let arr = []
        for (let k in data) {
            let str = k + '=' + data[k]
            arr.push(str)
        }
        return arr.join('&')
	}
    // let ss = resolveData({num: 5})
    // console.log(ss);

    // 模拟 $.ajax() ,自己封装一个实现ajax请求的函数
		/*
            option 是一个对象
                option.method 表示请求类型
                option.url  表示请求的url
                option.data 表示请求参数
                option.success()  表示一个处理服务器返回结果的函数
        */
    function ajax(options) {
        let xhr = new XMLHttpRequest();
        // 把传过来的参数转化为 查询拼接字符串
        let qs = resolveData(options.data)
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status == 200) {
                let result = JSON.parse(xhr.responseText) // 对象
                options.success(result)
            }
        }

        // toUpperCase()转化为大写
        if (options.method.toUpperCase() === 'GET') {
            // GET请求
            xhr.open('GET', options.url + '?' + qs)
            xhr.send()
        } else if (options.method.toUpperCase() === 'POST') {
            // POST请求
            xhr.open('POST', options.url)
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
            xhr.send(qs)
        }
	}
</script>
<script>
    ajax({
        method: 'geT',
        url: 'https://autumnfish.cn/api/joke/list',
        data: {
            num: 5
    	},
    	success: function(res) {
        	console.log(res);
    	}
	})

    ajax({
        method: 'post',
        url: 'http://www.liulongbin.top:3006/api/addbook',
        data: {
            bookname: '唐诗三百首',
            author: '老汤',
            publisher: '黑马出版社'
        },
        success: function(res) {
            console.log(res);
        }
    })
</script>

10.4 XMLHttpRequest Level2的新特性

旧版XMLHttpRequest缺点

①只支持文本数据传输,无法用来读取和上传

②传送和接收数据是,没有进度条,只能提示有没有完成

XMLHttpRequest Level2的新功能

① 可以设置HTTP请求权限

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

③ 可以上传

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

timeout、FormData、xhr.upload.onprogress、xhr.upload.onload

<body>
    <form id="form1">
        <!-- autocomplete="off" 阻止自动填充 -->
        <input type="text" name="uname" autocomplete="off">
        <input type="password" name="upwd">
        <button type="submit">提交</button>
    </form>
    <br>

    <input type="file" id="file1" />
    <br>
    <button id="btnUpload">上传文件</button>
    <br>
    <!-- 进度条 -->
    <div class="progress" style="width: 600px; margin-top: 10px;">
        <div class="progress-bar progress-bar-striped active" style="width: 45%" id="percent">
            45%
        </div>
    </div>
    <br>
    <img src="" alt="" id="img" width="800">
    <script>
        let xhr = new XMLHttpRequest()
        // // 设置超时时间
        // xhr.timeout = 30
        // // 添加timeout事件
        // xhr.ontimeout = function(e) {
        //     alert('请求超时了!')
        // }

        // 获取表单元素
        let form = document.querySelector('#form1')

        form.addEventListener('submit', function (e) {
            e.preventDefault()
            // 创建FormData对象 并接受form中数据
            let fd = new FormData(form)
            xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata')
            xhr.send(fd)
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    console.log(JSON.parse(xhr.responseText));
                }
            }
        })


        // // 创建FormData对象
        // let fd = new FormData()
        // // 为FormData添加表单项
        // fd.append('uname', 'ssy')
        // fd.append('upwd', '121212')

        // xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata')
        // // 可以省略xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        // xhr.send(fd)
        // xhr.onreadystatechange= function() {
        //     if (xhr.readyState === 4 && xhr.status === 200) {
        //         console.log(JSON.parse(xhr.responseText));
        //     }
        // }


        // 获取上传文件按钮
        let btnUpload = document.querySelector('#btnUpload')
        btnUpload.addEventListener('click', function () {
            let files = document.querySelector('#file1').files
            if (files.length <= 0) {
                return alert('请选择要上传的文件!')
            } else {
                // 创建FormData对象
                let fd = new FormData()
                // 为FormData添加表单项
                fd.append('avatar', files[0])

                let xhr = new XMLHttpRequest()
                xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar')
                xhr.send(fd)

                // 监听文件上传进度
                xhr.upload.onprogress = function (e) {
                    // e.lengthComputable是一个布尔值,表示当前上传的资源是否具有可计算的长度
                    if (e.lengthComputable) {
                        // e.loaded 已传输的字节
                        // e.total 需传输德总字节
                        // ceil()取整
                        let percentComplete = Math.ceil((e.loaded / e.total) * 100)
                        console.log(percentComplete);
                        document.querySelector('#percent').style.width = percentComplete + '%'
                        document.querySelector('#percent').innerHTML = percentComplete + '%'
                    }
                }

                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4 && xhr.status === 200) {
                        data = SON.parse(xhr.responseText)
                        console.log(data);
                        if (data.status == 200) {  // 成功上传
                            // 将服务器返回的图片地址。设置为img标签的src属性
                            document.querySelector('#img').src = 'http://www.liulongbin.top:3006' + data.url
                        } else {
                            console.log(data.message);
                        }
                    }
                }

                xhr.upload.onload = function () {
                    let percent = document.querySelector('#percent')
                    percent.classList.remove('progress-bar-striped')
                    percent.classList.add('progress-bar-success')
                }
            }
        })

    </script>
</body>

10.5 jQuery高级用法

10.5.1 jQuery实现文件上传
<!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>jQuery高级用法</title>
    <script src="js/jquery.min.js"></script>
</head>
<body>
    <input type="file" id="file1">
    <button id="btnUpload">上传文件</button>
    <script>
        $('#btnUpload').on('click', function () {
            // 转化为原生的对象
            let files = $('#file1')[0].files
            if (files.length <= 0) {
                return alert('请选择文件后再上传!')
            }
            let fd = new FormData()
            fd.append('avatar', files[0])
            $.ajax({
                method: 'POST',
                url: 'http://www.liulongbin.top:3306/api/upload/avatar',
                data: fd,
                // 不修改 Content-Type 属性,使用 FormData 默认的 Content-Type 值
                processData: false,
                // 不对 FormData 中的数据进行 url 编码,而是将 FormData 数据原样发送到服务器
                contentType: false,
                success: function(res) {
                    console.log(res);
                }
            })
        })
    </script>
</body>
</html>
10.5.2 jQuery实现loading效果

① ajaxStart(callback)

Ajax请求开始时,执行ajaxStart函数。可以在ajaxStart的callback中显示loading效果

注意:$(document).ajaxStart()函数会监听当前文档内所有的Ajax请求

② ajaxStop(callback)

Ajax请求结束时,执行ajaxStop函数。可以在ajaxStop的callback中隐藏loading效果

③ $.ajaxPrefilter();
在每个ajax请求发送之前,自定义ajax的选项或修改ajax的选项。

$.ajaxPrefilter(function (options) {
    // options是当前ajax请求的所有选项
    // 得到ajax请求的所有选项之后,可以对他进行修改
});
<!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>jQuery高级用法</title>
    <script src="js/jquery.min.js"></script>
</head>
<body>
    <input type="file" id="file1">
    <button id="btnUpload">上传文件</button>

    <br>
    <img src="images/loaing机械.gif" alt="" style="display: none;" id="loading">
    <script>
        $('#btnUpload').on('click', function () {
            // 转化为原生的对象
            let files = $('#file1')[0].files
            if (files.length <= 0) {
                return alert('请选择文件后再上传!')
            }
            let fd = new FormData()
            fd.append('avatar', files[0])
            $.ajax({
                method: 'POST',
                url: 'http://www.liulongbin.top:3006/api/upload/avatar',
                data: fd,
                // 不修改 Content-Type 属性,使用 FormData 默认的 Content-Type 值
                processData: false,
                // 不对 FormData 中的数据进行 url 编码,而是将 FormData 数据原样发送到服务器
                contentType: false,
                success: function (res) {
                    console.log(res);
                }
            })
        })

        // 监听ajax请求,自 jQuery 版本 1.8 起,该方法只能被附加到文档
        $(document).ajaxStart(function () {
            $('#loading').show()
        })
        $(document).ajaxStop(function () {
            $('#loading').hide()
        })
    </script>
</body>
</html>

10.6 axios

Axios是专注于网络数据请求的库,比原生的 XMLHttpRequest 对象,axios简单易用,比jQuery、axios更加轻量化,它只专注于网络数据请求

10.6.2 axios发起GET请求
axios.get('url', {params: {/*参数*/}}).then(callback)
10.6.2 axios发起POST请求
axios.post('url', {/*数据*/}).then(callback)
10.6.3 直接用axios发起请求
axios({
	method: '请求类型',
	url: '请求的URL地址',
	data: {/* POST数据 */},
	params: {/* GET参数 */}
}).then(callback)
10.6.4 案例
<!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>axios</title>
    <script src="js/axios.js"></script>
</head>
<body>
    <button id="btn1">发起GET请求</button>
    <button id="btn2">发起POST请求</button>
    <button id="btn3">直接使用ajax发起GET请求</button>
    <button id="btn4">直接使用ajax发起POST请求</button>

    <script>
        document.querySelector('#btn1').addEventListener('click', function() {
            let url = 'http://www.liulongbin.top:3006/api/get'
            let paramsObj = {name: 'ss', age: 20}
            axios.get(url, {params: paramsObj}).then(function(res) {
                console.log(res);
                console.log(res.data);
            })
        })

        document.querySelector('#btn2').addEventListener('click', function() {
            let url = 'http://www.liulongbin.top:3006/api/post'
            let dataObj = {address: '北京', location: '顺义区'}
            axios.post(url, dataObj).then(function(res) {
                console.log(res);
                console.log(res.data);
            })
        })

        document.querySelector('#btn3').addEventListener('click', function() {
            let url = 'http://www.liulongbin.top:3006/api/get'
            let paramsObj = {name: 'ss', age: 20}
            axios({
                method: 'GET',
                url: url,
                params: paramsObj
            }).then(function(res) {
                console.log(res);
                console.log(res.data);
            })
        })

        document.querySelector('#btn4').addEventListener('click', function() {
            let url = 'http://www.liulongbin.top:3006/api/post'
            let dataObj = {address: '北京', location: '顺义区'}
            axios({
                method: 'POST',
                url: url,
                params: dataObj
            }).then(function(res) {
                console.log(res);
                console.log(res.data);
            })
        })
    </script>
</body>
</html>

十一、跨域与JSONP

11.1 了解同源策略和跨域

同源策略是浏览器提供的一个安全功能

两个页面的协议域名端口都相同,则两个页面具有相同的源,即同源,反之为跨域

11.1.1 跨域

跨域拦截

同源策略跨域拦截

跨域数据请求

JSONP:早期临时方案,只支持GET请求,和Ajax没有关系

CORS:两种都可以,不兼容低版本浏览器

11.2 JSONP

实现原理:JSONP通过

<script>
    // $.ajax({
    //     method: 'GET',
    //     url: 'http://ajax.frontend.itheima.net:3006/api/jsonp',
    //     data: {
    //         name: 'ss',
    //         age: 20
    //     },
    //     success: function(res) {
    //         console.log(res);
    //     }
    // })

    function success(data) {
        console.log('调用成功!');
        console.log(data);
	}
</script>
// <script src="js/getdata.js?callback=success"></script>
<script src="http://ajax.frontend.itheima.net:3006/api/jsonp?callback=success&name=ls&age=30"></script>

JQuery中的JSONP(ajax)

$.ajax({
    url: 'http://ajax.frontend.itheima.net:3006/api/jsonp?name=zs&age=20',
    // 如果要使用 $.ajax() 发起 JSONP 请求,必须指定 datatype 为 jsonp
    dataType: 'jsonp',
    jsonp: 'ss',
    jsonpCallback: 'hh',
    success: function(res) {
        console.log(res);
    }
})

自动携带:callback=jQueryxxx,jQueryxxx随机生成的回调函数名称

JQuery中的JSONP过程

  • 在发起JSONP请求的时候,动态向
    中append一个

11.3 案例-淘宝搜索

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>淘宝搜素</title>
    <!-- 导入页面的基本样式 -->
    <link rel="stylesheet" href="css/search.css" />
    <!-- 导入 jQuery -->
    <script src="js/jquery.min.js"></script>
    <!-- 导入模板引擎 -->
    <script src="js/template-web.js"></script>
</head>
<body>
    <div class="container">
        <!-- Logo -->
        <img src="images/taobao_logo.png" alt="" class="logo" />

        <div class="box">
            <!-- tab 栏 -->
            <div class="tabs">
                <div class="tab-active">宝贝</div>
                <div>店铺</div>
            </div>
            <!-- 搜索区域(搜索框和搜索按钮) -->
            <div class="search-box">
                <input id="ipt" type="text" class="ipt" placeholder="请输入要搜索的内容" /><button class="btnSearch">搜索</button>
            </div>
            <!-- 搜索建议列表 -->
            <div id="suggest-list"></div>
        </div>
    </div>

    <!-- 模板结构 -->
    <script type="text/html" id="tpl-suggestList">
        {{each result}}
        <div class="suggest-item">{{$value[0]}}</div>
        {{/each}}
    </script>

    <script>
        $(function () {
            // 1、定义防抖的延时器 timer
            var timer = null;
            // 定义全局缓存对象
            var cacheObj = {};
            // 定义防抖函数,延时500毫秒后,再请求数据接口
            function debounceSearch(kw) {
                // 开启延时器
                timer = setTimeout(function () {
                    getSuggestList(kw);
                }, 500)
            }

            // 为输入框绑定Keyup事件
            $('#ipt').on('keyup', function () {
                // 清空timer
                clearTimeout(timer);

                var keywords = $(this).val().trim();
                if (keywords.length <= 0) {
                    // 如果用户没有输入内容,则退出并清空隐藏列表
                    return $('#suggest-list').empty().hide()
                }
                // 发起请求之前,先判断缓存里是否有数据
                if (cacheObj[keywords]) {
                    return renderSuggestList(cacheObj[keywords]);
                }
                // TODO:获取搜索建议列表
                // console.log(keywords);
                // 调用自定义函数
                // getSuggestList(keywords);
                debounceSearch(keywords);

            })
            // 获取搜索建议的函数封装
            function getSuggestList(kw) {
                $.ajax({
                    url: 'https://suggest.taobao.com/sug?q=' + kw,
                    // 发起JSONP请求
                    dataType: 'jsonp',
                    success: function (res) {
                        renderSuggestList(res);
                    }
                })
            }

            // 渲染UI结构
            // 传入一个res参数(res就是待渲染的数据)
            function renderSuggestList(res) {
                // 判断是否有待渲染的数据,如果没有就return出去,清空列表并隐藏
                if (res.result.length <= 0) {
                    return $('#suggest-list').empty().hide();
                }
                // 调用模板引擎的template函数
                var htmlstr = template('tpl-suggestList', res); // 返回一个渲染好的html结构
                // 将渲染好的字符串放到搜索建议列表div中,并展示出来
                $('#suggest-list').html(htmlstr).show();

                // 1、获取到用户输入的内容,当做键
                var k = $('#ipt').val().trim();
                // 2、将数据做为值缓存到全局对象中
                cacheObj[k] = res;
            }
        })
    </script>
</body>
</html>

11.4 防抖和节流

防抖策略是当事件被触发后,延迟n秒后再执行回调,如果在这n秒内事件又被触发,则重新计时

节流策略,可以减少一段时间内事件触发频率。

鼠标跟随节流

<!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>
    <script src="js/jquery.min.js"></script>
    <style>
        html,
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }

        #angle {
            position: absolute;
        }
    </style>
</head>
<body>
    <img src="images/angle.gif" alt="" id="angle">
    <script>
        $(function() {
            // 1.获取图片
            var angle = $('#angle');

            var timer = null; // 步骤1:定义节流阀

            // 2.绑定mousemove事件
            $(document).on('mousemove', function(e) {
                // 步骤3:判断节流阀是否为空
                if (timer) {
                    return;
                } else {
                    // 步骤2:开启延时器
                    timer = setTimeout(function() {
                        $(angle).css('top', e.pageY + 'px').css('left', e.pageX + 'px');
                        console.log('ok');
                        timer = null;
                    }, 16);
                }
            });
        });
    </script>
</body>
</html>

原生XHR

jQueryXHR

十二、HTTP协议加强

12.1 HTTP协议简介

什么是http协议?

  • 超文本传输协议

http协议规定了什么?

  • 规定了客户端和服务器交互数据的时候,数据要遵守的格式。
  • 请求/响应 模式

12.2 http请求消息/请求报文

请求报文,指的是客户端提交给服务器的全部数据。

  • 请求行
    • 请求方式
    • 请求url地址
    • 协议及版本
  • 请求头
    • 键:值 的形式,一次请求,可能会有很多请求头
    • Content-Type: ‘application/x-www-form-urlencoded’ / ‘multipart/form-data; xxx’
      • 提交的数据的一种编码格式(查询字符串格式 / FormData格式)
    • User-Agent: ‘’
      • 体现了当前客户端是什么浏览器、版本是什么版本

请求头部

  • 空行

    • 分隔作用
  • 请求体

    • xhr.send(请求体); 客户端提交的数据就是请求体
    • GET方式没有请求体
    • POST方式 才有请求体

12.3 http响应消息/响应报文

  • 响应行

    • 协议及版本
    • http状态码 (比如, 200)
    • http状态描述信息 (比如, OK)
  • 响应头

    • Access-Control-Allow-Origin: ‘*’

      • CORS跨域资源共享,服务器设置的响应头,允许ajax跨域
    • Content-Type: ‘application/json; charset=utf-8’

      • 服务器告诉客户端,返回的数据是什么格式的,编码是什么编码

        $.get(url, data, callback, dataType);
        - dataType 表示预期服务器返回的类型
        
        jQuery如何知道服务器返回的数据类型呢?是根据响应头中的Content-Type判断,如果判断服务器返回的是json格式,所以jQuery内部就会自动调用JSON.parse()将JSON格式的数据转成JS数组或对象
        
        $.get(url, data, function (res) {
            如果响应头没有没有Content-Type,则res 就是字符串
            手动指定最后一个dataType为json之后,res就是JS对象
        });
        
  • 空行

    • 分隔作用
  • 响应体

    • 服务器返回的主体内容。
    • 就是我们之前所说的,服务器返回的数据。

12.4 请求方式

请求方法

12.5 http响应状态码

  • 200(OK)表示请求成功
  • 201(Created)请求成功,一般用于添加资源成功
  • 304 (Not Modified)表示请求的资源没有修改(第一次请求)
  • 400(Bad Request)语法有误(一般来说,请求参数写错了或者请求头写错了)
  • 403(Forbidden)访问成功了,但是服务器告诉你没有权限访问
  • 404(Not Found)请求的资源,在服务器上不存在,找不到。
  • 500()服务器内部错误

响应状态码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pluto_ssy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值