Ajax异步

Ajax

一、Ajax初识

Ajax的全称叫做异步JavaScript和XML

  • 在网页中利用XMLHttpRequest对象和服务器进行数据交互的方式,就是Ajax
  • Ajax可以实现网页服务器之间的数据交互,且不需要刷新页面。
  • Ajax可以在接受服务器数据的同时还可以进行页面其他操作

二、jQuery发起Ajax

jQuery中的Ajax

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

jQuery中发起Ajax请求最常用的三个方法如下:

  • $.get()
  • $.post()
  • $.ajax()

2.1 $.get()

作用:

  • 专门发起get请求,用于将服务端的资源请求到客户端。

格式:

  • $.get(url,[data],[callback])
    

    url: 资源地址

    data:可选参数,get请求携带的参数

    callback:可选,请求成功的回调函数

发起不带参数的请求

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

<button class="btn-success btn">发送GET请求</button>

<script>
    //页面加载完执行
    $(function () {
        //给按钮绑定点击事件
        $('.btn').on('click',function () {
            //事件触发则发送GET请求,以下链接为接口测试平台,用于响应并返回我们的Ajax请求。
            $.get('http://www.escook.cn:3006/api/getbooks',function (response) {
                //回调函数有个形参,可以接收响应的信息,叫response、res都行,自定义。
                console.log(response)
            })
        })
    })
</script>

<!--
控制台输出:
	{status: 200, msg: '获取图书列表成功', data: Array(6)}
	数据获取成功。
-->

如何查看是否成功发送请求,以及服务端是否成功返回数据

  • 浏览器F12---->Network---->XHR---->Headers
  • 浏览器F12---->Network---->XHR---->Preview

image-20220726195016171

发起带参数的请求

上一小节,我们利用$.get()向服务端成功的发起get请求,而服务端也是返回了data数组,携带参数可以实现只让服务端返回自己想要的那么一组数据,而不是全部。

$.get('http://www.escook.cn:3006/api/getbooks',{id:1},function (response) {
console.log(response)
})

2.2 $.post()

作用:

  • 用来专门发起post请求,从而向服务器提交数据

格式:

  • $.post(url,[data],[callback])
    

    url:提交数据的地址

    data:要提交的数据

    callback:数据提交成功的回调函数

代码示例:

//提交数据的地址
$.post('http://www.escook.cn:3006/api/addbook',
       //要提交的数据
       { bookname:'Ajax',author:'liuyu',publisher:'老刘出版社'},
       //回调函数,查询请求返回信息。
       function (response) {
    		console.log(response)
    		//控制台输出:{"status":201,"msg":"添加成功"}
		}
	)

查询post提交的数据

image-20220727015237522

查询请求头部信息(是否请求成功): 略,与get一致。

查询请求返回信息:略。

2.3 $.ajax()

作用:

  • 相比 . g e t ( ) 与 .get()与 .get().post(),jQuery中提供的$.ajax()不局限于只能发送一种请求,允许我们对Ajax请求进行更详细的配置。

格式:

  • $.ajax({
        type:"",
        url:"",
        data:{},
        success:function(res){}
    })
    

    type:请求的方式,例如GET或者POST

    url:请求的URL地址

    data:本次请求要携带的数据或参数

    success:请求成功之后的回调函数

代码示例:

获取id为1的数据

<button class="btn-success btn">发送请求</button>
$(function () {
$('.btn').on('click',function () {
$.ajax({
type:'GET',
url:'http://www.escook.cn:3006/api/getbooks',
data:{id:1}
})
})
})

2.4 接口调试工具

接口的概念:

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

接口请求流程:

  • 用户希望获取数据,网页呢就利用Ajax向服务端发起请求,服务端收到请求后响应处理

接口测试工具Apipost,优点:不需要书写代码即可检验接口是否可以处理请求。

Apipost官网:https://www.apipost.cn/

利用接口测试工具,对接口发起GET请求:

image-20220727025601141

获取ID为1的数据

image-20220727025838448

发送POST请求:

image-20220727155304340

注:

  • POST请求提交数据时,需要在Body处填写
  • 如果返回的响应状态码为502,那么需要修改编码为urlencoded,默认的form-data是发送文件的。

三、案例-图书管理系统

3.1 布局

利用BootStrap可轻松完成布局,以下为布局思路。

官网:https://v3.bootcss.com/

下载并引入BootStrap.css与BootStrap.js即可

注:

  • BootStrap组件依赖于jQuery,需要先引入jQuery.js

先来看看最终效果:

image-20220727191625671

(仅作参考)

在BootStrap文档中,找到组件,由于只涉及到调用接口进行增删改查,所以不需要用到导航条啊之类的,所以这里选用的是面板

image-20220727191929440

有了个大面板之后,就需要在里面书写input标签和table标签了,而bootstrap的全局CSS样式中,有写好的demo,我们直接复制修改即可。

image-20220727192204229

这里选用的样式为上图,删除自己不想要的,然后文本框相关的多复制两份即可。

表格这里不过多说明,文档中提供了很多table标签的样式,有需要直接添加类名即可。

源码:

<!--面板-->
<div class="panel panel-info">
<div class="panel-heading">添加新图书</div>
<div class="panel-body">
<!--面板内部搜索栏-->
<form class="form-inline">
<!--书名表单-->
<div class="form-group">
   <div class="input-group">
       <div class="input-group-addon">书名</div>
       <input type="text" class="form-control" id="bookname" placeholder="请输入书名">
   </div>
</div>

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

<!--出版社表单-->
<div class="form-group">
   <div class="input-group">
       <div class="input-group-addon">出版社</div>
       <input type="text" class="form-control" id="publisher" placeholder="请输入出版社">
   </div>
</div>

<!--提交-->
<button type="submit" class="btn btn-success" id="addbook">添加</button>
</form>
<!--面板内部搜索栏-->

<!--图书表格-->
<table class="table table table-striped table-bordered table-hover table-condensed">
<thead>
 <tr class="success">
        <th>ID</th>
        <th>书名</th>
        <th>作者</th>
        <th>出版社</th>
        <th>操作</th>

</thead>
<tbody>
<!--                <tr>-->
<!--                    <td>1</td>-->
<!--                    <td>1</td>-->
<!--                    <td>1</td>-->
<!--                    <td>1</td>-->
<!--                    <td>1</td>-->
<!--                </tr>-->
</tbody>
</table>
<!--图书表格-->
</div>
</div>
<!--面板-->

3.1.1 禁止form表单提交

详情请见第5.3章节,本章节主要以完成项目为主,进行“插播”

我们的表单使用的bootstrap的CSS样式,而该样式的demo中使用的是form表单,form与Ajax不可混用,否则会出现提交数据时,无法正常提交的现象。

所以我们有两种方案:

  • 自己去手写,不使用bootstrap现成的。

  • 关闭form表单的默认提交事件。

    //阻止form表单的默认提交
    $('form').on('submit',function (e) {
        e.preventDefault();
    })
    

3.2 渲染页面

一、渲染图书列表

已知调用接口返回的信息为:

  • {"status":200,"msg":"获取图书列表成功","data":[{"id":1,......},{"id":2,......}]
    

通过回调函数的参数,我们获取服务端的响应数据,所以可以判断,当返回的状态码status为200时,获取key为data的值,该值为数组,每个数组的元素,为记录图书信息的对象,有了对象,那我们就可以从中取出书名作者名等数据,随后渲染在页面上。

那么问题来了,如何渲染到页面上?

小测试:

<div></div>

<script>
let span = "<span>我是div下的span</span>"
$('div').html(span)
</script>
  • html方法是设置内部内容的,因为可以识别标签,所以直接书写HTML代码,以字符串传入到html方法。

我们都知道,需要渲染的数据是table下的tbody下的tr和td标签,那我们可以将获取到的数据利用字符串拼接的方式,书写符合html格式的字符串,随后利用append或者html等方法来实现最终效果。

验证绑定获取图书列表标签,是否可以渲染到页面

//定义函数。获取图书列表
function getList() {
    //发送get请求
    $.ajax({
        type:'GET',
        url:'http://www.escook.cn:3006/api/getbooks',
        //回调函数
        success:function (res) {
            //后端返回的数据中,status表示状态码,当GET请求失败的时候发出弹框。
            if (res.status !== 200) {
                return alert('图书列表获取失败')
            }
            //因为每次刷新、添加、删除都需要调用该函数渲染
            //所以在每次渲染之前,先把之前的数据删除掉。
            $('tbody').empty()
            
            //创建一个空数组,随后把每一个图书数据对象提取出数据,最后利用字符串拼接的方式,
            //将数据拼接为html格式的字符串,把每个图书的字符串添加到列表中。
            let rows = []
            //res.data是数组套对象的形式存储了数据,each遍历可以拿到每个图书对象。
            $.each(res.data,function (i,item) {
                // i为对象在列表中的索引,item为图书对象。
                rows.push('<tr><td>'+item.id+'</td><td>'+ item.bookname +'</td><td>'+ item.author +'</td><td>'+ item.publisher +'</td><td><a class="btn btn-danger" data-id='+item.id+'>删除</a></td></tr>')
                //这里的删除标签添加了自定义属性data-id,值为书籍的ID,后续可用来删除,其中类btn和btn-danger为bootstrap样式。
                
            })
			//join列表转字符串,随后插入到tbody中,完成渲染。
            $('tbody').html(rows.join(''))
        }
    })
}

//调用函数,将数据渲染到页面上
getList()

最终效果:
image-20220729205148677

3.2 实现删除功能

注意事项:

  • 由于我们的删除a标签,是动态创建出来的,所以不能直接绑定事件。
  • 需要利用到事件委托,通过给父元素绑定事件,随后委托给子元素,也就是这个a标签。

代码:

//委托给a标签,删除按钮,删除按钮的类为btn-danger
$('tbody').on('click','.btn-danger',function () {
//利用attr获取触发事件对象的自定义属性data-id
let delId = $(this).attr('data-id')
//调用api
$.ajax({
type:'GET',
url:"http://www.escook.cn:3006/api/delbook",
//根据接口文档传入想要删除书籍的ID
data:{id:delId},
//回调函数
success:function (res) {
if (res.status !== 200) return alert('删除失败,请重试!')
//重新渲染
getList()
}
})
})

3.4 实现添加功能

思路:

  • 先获取input框输入的数据,随后将数据以post请求的方式提交到后端。

API接口测试:
image-20220730205942040

代码:

//添加图书
$('#addbook').on('click',function () {
    //获取input框中的数据,并去掉首尾的空格。
    let bookName = $('#bookname').val().trim()
    let bookAuthor = $('#author').val().trim()
    let bookPublisher = $('#publisher').val().trim()

    //判断是否为空,只有三个框内有一个没有数据,则不往下执行。
    if (bookName.length == 0 || bookAuthor.length == 0 || bookPublisher.length == 0) {
        return alert('请将表单填写完整')
    }
    //POST请求
    $.ajax({
        type:'POST',
        url:'http://www.escook.cn:3006/api/addbook',
        data: {bookname:bookName,author:bookAuthor,publisher:bookPublisher},
        success:function (response) {
            //正确响应状态码为201
            if (response.status !== 201) {
                return alert('添加失败,请重试!')
            }
            //提交成功后,重新渲染下数据
            getList()
            //把input表单的内容清空
            $('#iptbookname').val('')
            $('#iptauthor').val('')
            $('#iptpublisher').val('')
        }
    })


})

四、案例-聊天机器人

'''
	本章节只提供接口地址以及使用说明,源码暂无。
'''

机器人接口

接口地址: http://www.liulongbin.top:3006/api/robot

image-20220810154323459
  • 请求方式: GET

  • 参数: spoken

  • 值:获取表单输入的信息

  • info中text对象为机器人返回的回复信息。

机器人语音接口

接口地址: http://www.liulongbin.top:3006/api/synthesize

image-20220810154925451
  • 请求方式: GET

  • 参数: text

  • 值:获取表单输入的信息

  • voiceUrl为返回的音频地址,将改值赋给音频标签的src并设置自动播放即可。

五、form表单

什么是表单:

  • 表单在网页上主要负责数据采集功能
  • HTML中的<form>标签,就是用于采集用户输入的信息,并通过<form>标签的提交操作,吧采集到的信息,提交到服务端进行处理。

表单的组成部分

  • 表单标签<form>
  • 表单域,包含了文本框、密码框、隐藏框、多行文本框、复选/单选框、文件上传框、下拉选择框等。

5.1 form标签的属性

5.1.1 action属性

<form>标签的属性

一、action

  • action属性用来规定当提交表单时,向何处发送表单数据
  • action属性的值,应该是后端提供的一个URL地址,这个URL地址专门负责收集表单提交过来的数据。
  • 当form表单在未指定action属性值的情况下,action的默认值为当前页面的URL地址。

注意:

  • 当提交表单后,页面会立即跳转到action属性,指定的URL地址。

示例:

当不指定action时

<form action="">
用户名:<input type="text" name="iptUname">
密码: <input type="password" name="iptUpassword">
<button type="submit">提交</button>
</form>
  • 点击提交浏览器URL地址变为:

    http://....form测试.html?iptUname='表单输入的信息'iptUpassword='表单输入的信息'
    

当指定action时

<!--提交到Login页面-->
<form action="/login">
	......略
</form>
  • 点击提交浏览器URL地址变为:

    http://....login?iptUname='表单输入的信息'iptUpassword='表单输入的信息'
    

5.1.2 target属性

<form>标签的属性

二、target

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

  • 该属性的值有5个,默认情况下target的值是_self,表示在相同的框架中打开action URL。

    描述
    _blank在新窗口中打开,另开标签页
    _self默认,在相同的窗口中打开,覆盖。
    _parent在父窗口中打开(很少用)
    _top在整个窗口中打开(很少用)
    framename在指定的窗口中打开(很少用)

当我们修改了action值之后,由于默认提交就会跳转到action值这个页面,并且是直接在该页面跳转,如果不想覆盖掉现在的网页,那么可以添加target属性,值为_blank,这样会另外开设标签页。

5.1.3 method属性

<form>标签的属性

三、method

  • method属性用来规定以何种方式把表单数据提交到action URL。
  • 它的可选值有两个,分别是get和post
  • 默认情况下,method的值为get,表示通过URL地址的形式,把表单数据提交到action URL。

get方式适合用来提交少量的、简单的数据。

post方式适合用来提交大量的、复杂的、或者包含文件上传的数据。相对来说post会比get安全一点。

在实际开发中,form表单的post提交方式用的最多,很少用get,例如登陆、注册、添加数据等表单操作,都需要使用post方式来提交表单。

5.1.4 enctype属性

<form>标签的属性

四、enctype

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

  • 它的可选值有三个,默认情况下,enctype的值为application/x-www-form-urlencoded,表示在发送前编码所有的字符。

    描述
    application/x-www-form-urlencoded在发送前编码所有字符(默认)
    multipart/form-data不对字符编码,在使用包含文件上传控件的表单时,必须使用改值
    text/plain空格转换为“+”加号,但不对特殊字符编码(很少用)

简单来说就是:

  • 如果涉及到文件上传的操作时,必须将enctype的值设置为multipart/form-data
  • 如果表单的提交不涉及到文件上传,则直接将enctype的值设置为application/x-www-form-urlencoded即可。

5.2 表单的同步提交

什么是表单的同步提交:

  • 通过点击submit按钮,触发表单提交的操作,从而使页面跳转到action URL的行为,叫做表单的同步提交。

表单同步提交的缺点:

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

解决方案:

  • 表单只负责采集数据,Ajax负责将数据提交到服务器。

5.3 通过Ajax提交表单数据

在jQuery中,可以使用下列两种方式绑定事件,利用事件绑定来监听到表单的提交事件:

//第一种
$('form').submit(function() {})
//第二种
$('form').on('submit',function() {})

只是监听到了提交事件,但并不能阻止表单的默认行为,也就是页面跳转不能阻止,如果让它跳转了,那么页面之前的状态和数据还是会丢失。

阻止表单默认提交行为

当监听到表单的提交事件以后,可以调用事件对象的 e.preventDefault() 函数,来阻止表单的提交和页面的跳转,示例代码如下:

$('form').on('submit',function(e) {
e.preventDefault()
})

此时刷新页面将不再跳转,数据也仍然会保留在页面上。

5.4 serialize函数

作用:

  • 可以一次性获取所有表单的数据。

格式:

  • $('form').serialize()
    

注意:

  • 在使用serialize()函数快速获取表单数据时,必须为每个表单添加name属性

代码示例:

<form action="">
    用户名:<input type="text" name="iptUname">
    密码: <input type="password" name="iptUpassword">
    <button type="submit">提交</button>
</form>
$('form').on('submit',function(e) {
    e.preventDefault()

    let data = $('form').serialize()  //获取表单的数据
    console.log(data)
})

六、模板引擎

案例中渲染UI结构是遇到的问题:

$.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 class="btn btn-danger" data-id='+item.id+'>删除</a></td></tr>')
})

$('tbody').html(rows.join(''))

上述代码是通过字符串拼接的形式,来渲染UI结构的。

弊端:

  • 如果UI结构比较复杂,则拼接字符串的时候需要格外注意引号之间的嵌套。且一旦需求发生变化,修改起来非常麻烦

什么是模板引擎:

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

    |---------|
    |  '数据'  |---------|
    |---------|			 |	   |---------|	   |----------|
    					 |-----|'模板引擎'|-----| 'HTML页面'|
    |---------|			 |     |---------|     |----------|
    |'模板结构' |---------|
    |---------|
    

模板引擎的好处:

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

6.1 art-template模板引擎

简介:

  • art-template是一个简约、超快的模板引擎,中文官网首页如下:

    http://aui.github.io/art-template/zh-cn/index.html

安装art-template

第一步: 进入安装页面

http://aui.github.io/art-template/zh-cn/docs/installation.html

第二步: 找到下载:template-web.js

  • 随后双击打开文件,Ctrl+A复制,然后粘贴到空的.js文件中。
  • 或者,鼠标右键,然后选择将链接另存为即可。

6.2 如何使用模板引擎

如何使用

  • 第一步:导入模板引擎(js文件)
  • 第二步:定义数据
  • 第三步:定义模板
  • 第四步:调用template函数
  • 第五步:渲染HTML结构

代码示例:

第一步,导入模板引擎

<!--导入模板引擎-->
<script src="js/template-web.js"></script>
<!--导入之后,会在全局多出一个函数,叫做template('模板的ID','需要渲染的数据对象')-->

第二步,定义数据

//在数组中定义两个学生信息对象
let stuMsg = [{
'id':'1',
'name':'liuyu',
'age':22,
'school':'中国地质大学(北京)',
'college':'信息工程学院',
},{
'id':'2',
'name':'夏之悦',
'age':22,
'school':'北京大学',
'college':'光华管理学院',
}]

第三步,定义模板

<!--
第一:
	script标签默认的type是text/JavaScript,意为当前标签下的代码当做JS代码处理
而我们要写的模板,而不是js代码,所以这里需要改为text/html。
第二:
	因为第四步需要调用template函数,而该函数需要传入模板的ID,故创建ID,值为stuMsgTemplate学生信息模板。
-->
<script type="text/html" id="stuMsgTemplate">
<li>{{name}}</li>
<li>{{age}}</li>
<li>{{school}}</li>
<li>{{college}}</li>
<br>
</script>

第四步,调用template函数

//调用template,传入绑定的模板,以及模板要使用的数据,
//调用之后,会将模板内的{{}}占位符进行替换,替换成对应的数据,并返回替换之后的HTML代码,不过格式是str。
let stuHtmlStr1 = template('stuMsgTemplate',stuMsg[0])
console.log(stuHtmlStr1)

let stuHtmlStr2 = template('stuMsgTemplate',stuMsg[1])
console.log(stuHtmlStr2)

/*控制台输出:
<li>liuyu</li>
<li>22</li>
<li>中国地质大学(北京)</li>
<li>信息工程学院</li>
<br>
<li>夏之悦</li>
<li>22</li>
<li>北京大学</li>
<li>光华管理学院</li>
<br>
*/

第五步,渲染HTML

<div>学生信息表</div>
//获取到HTMLstr之后,插入到div标签下,完成渲染。
$('div').append(stuHtmlStr1).append(stuHtmlStr2)

优点:

  • 创建HTML模板之后,可以反复调用template方法生成HTML代码,随后插入到需要的地方。
  • 不需要再利用字符串拼接,这种复杂的方法。

6.3 art-template标准语法

什么是标准语法:

  • art-template提供了**{{}}这种语法格式,在{{}}内可以进行变量输出**,或循环数组等操作,这种**{{}}语法在art-template中被称作标准语法**。

6.3.1 标准输出

标准语法-输出

{{ value }}
{{ obj.key }}
{{ obj['key'] }}
{{ arr[index] }}
{{ a ? b : c }}
{{ a || b }}
{{ a + b }}

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

6.3.2 原文输出

标准语法-原文输出

{{ @ value }}

作用:

  • 当value的值,包含HTML标签结构时,如果要保证可以渲染到页面上,那么就需要使用原文输出,格式就是在前面添加一个**@**符号。

6.3.3 条件输出

标准语法-条件输出

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

作用:

  • 如果要实现条件输出,那么可以在{{}}中使用 if…elseif…/if 的方式,进行按需输出。

代码示例:

<script src="js/template-web.js"></script>
<div>学生信息表</div>
<script type="text/html" id="stuMsgTemplate">
    <li>{{name}}</li>
    <li>{{age}}</li>
    <li>{{school}}</li>
    <li>{{college}}</li>
    <li>
        {{if isGraduate === 1}}
        已毕业
        {{else if isGraduate ===0}}
        在读
        {{/if}}
    </li>
    <br>
</script>
<script>
    //定义两个学生信息对象
    let stuMsg = [{
        'id':'1',
        'name':'liuyu',
        'age':22,
        'school':'中国地质大学(北京)',
        'college':'信息工程学院',
        'isGraduate':1,  
    },{
        'id':'2',
        'name':'夏之悦',
        'age':22,
        'school':'北京大学',
        'college':'光华管理学院',
        'isGraduate':0,
    }]
    //调用template
    let stuHtmlStr1 = template('stuMsgTemplate',stuMsg[0])
    let stuHtmlStr2 = template('stuMsgTemplate',stuMsg[1])
    $('div').append(stuHtmlStr1).append(stuHtmlStr2)
</script>

最终效果:

image-20220811025810545

6.3.4 循环输出

标准语法-循环输出

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

作用:

  • 如果要实现循环输出,则可以在{{}}内,通过each语法循环数组,当前循环的索引使用 i n d e x 进行访问,当前的循环项使用 index进行访问,当前的循环项使用 index进行访问,当前的循环项使用value进行访问。

代码示例:(代码以上一章节为例,为节省篇幅,只展示修改的地方)

<script type="text/html" id="stuMsgTemplate">
    <li>姓名:{{name}}</li>
    <li>年龄:{{age}}</li>
    <li>学校:{{school}}</li>
    <li>学院:{{college}}</li>
    <li>是否在读:
        {{if isGraduate === 1}}
        已毕业
        {{else if isGraduate ===0}}
        在读
        {{/if}}
    </li>
    <li>爱好:
        {{ each hobby }}
            {{$value}}
        {{/each}}
    </li>
    <br>
</script>
let stuMsg = [{
    'id':'1',
    'name':'liuyu',
    'age':22,
    'school':'中国地质大学(北京)',
    'college':'信息工程学院',
    'isGraduate':1,
    'hobby':['吃饭','睡觉','打游戏'],
},{
    'id':'2',
    'name':'夏之悦',
    'age':22,
    'school':'北京大学',
    'college':'光华管理学院',
    'isGraduate':0,
    'hobby':['吃饭','美妆','刷抖音'],
}]

最终效果:

image-20220811031253674

6.3.5 过滤器

过滤器就好比是净水器:

自来水 --->  净水器  --->  纯净水
需要处理的值  --参数-->  过滤器函数  ---返回值-->  输出新值 

过滤器的本质,就是一个function函数

格式:

  • {{ value | 过滤器函数名 }}
    

    过滤器语法类似管道操作符,将value这个值,交给过滤器处理。

  • 定义过滤器的基本语法:

    template.defaults.imports.过滤器函数名 = function(value) {/*return处理的结果*/}
    

    与模板是配合使用的,模板通过绑定的data对象获取数据value,随后将获取到的value,丢给过滤器,过滤器的形参value接收到以后,进行一些操作之后,再return给模板得到value,最后展示在页面上的,就是最终处理好的数据。

代码示例:

需求:页面上显示当前的时间

一、第一步

  • 过滤器只是在渲染数据之前,再对数据做了一遍处理操作而已。

  • 所以我们首先要确保数据可以正常渲染。

    <!--导入模板引擎-->
    <script src="js/template-web.js"></script>
    
    <!--HTML代码-->
    <div>当前时间:</div>
    
    <!--模板-->
    <script type="text/html" id="tplDate">
        <span>{{ currentDate }}</span>
    </script>
    
    //定义数据
    let date = {currentDate:new Date()}
    //调用temlate
    let htmlStr = template('tplDate',date)
    //渲染到页面
    $('div').append(htmlStr)
    
  • 打开浏览器验证:

    '''
    浏览器页面内容:   当前时间: "2022-08-11T16:26:36.438Z"
    '''
    

    可以看到,时间格式很丑,我们只想要年月日的话,可以借助过滤器进行操作。

二、第二步

  • 因为模板的格式为:

    {{ value | 过滤器函数名 }}
    

    所以就有了:

    <script type="text/html" id="tplDate">
        <span>{{ currentDate | dateFormat }}</span>
    </script>
    

    这里的currentDate为数据对象的key(Date对象),dateFormat为过滤器函数的函数名。

  • 定义过滤器函数

    注意!!!! 一定要写在定义数据的上面!!!

    //形参date接收模板第一次收到的数据(未经处理过的)
    template.defaults.imports.dateFormat = function(date) {
        let y = date.getFullYear()
        let m = date.getMonth() + 1
        let d = date.getDate()
        return y+'-'+m+'-'+d    //必须有return,将值返回给currentDate
    }
    
  • 最终浏览器页面为:

    '''
    浏览器页面内容:   当前时间: 2022-8-12
    '''
    

6.4 模板引擎实现原理

模板引擎的实现原理,其实就是利用了正则表达式

正则与字符串操作

函数名:

  • exec()

作用:

  • exec()函数用于检索字符串中的正则表达式的匹配。
  • 如果字符串中有匹配的值,则返回该匹配的值,否则返回null

格式:

  • RegExpObject.exec(String)
    

代码示例:

let str = 'hello'
let reg = /o/
console.log(reg.exec(str))
	// ['o', index: 4, input: 'hello', groups: undefined] 

分组

正则表达式中,()小括号包起来的内容表示一个分组,可以通过分组来提取自己想要的内容,代码示例如下:

let str = '<div>我叫{{name}}}</div>'
// +表示匹配前面的子表达式一次或多次
let reg = /{{([a-zA-Z]+)}}/    
let result = reg.exec(str)
console.log(result)
// ['{{name}}', 'name', index: 7, input: '<div>我叫{{name}}}</div>', groups: undefined]

可以发现:

  • {{name}}为正则表达式匹配到。
  • name为通过分组获取到的内容 ([a-zA-Z]+)

字符串的replace函数

函数名:

  • replace() 字符串函数

作用:

  • 该函数为字符串内置方法,用于将一些字符替换另一些字符。

代码示例:

let str = '123456'.replace('123','ABC')
console.log(str)
	// ABC456

利用字符串替换,将正则匹配到的内容,替换成分组拿到的内容。

let str = '<div>我叫{{name}}</div>'
let reg = /{{([a-zA-Z]+)}}/    //+匹配前面的子表达式一次或多次
let result = reg.exec(str)
// result = ['{{name}}', 'name', index: 7, input: '<div>我叫{{name}}}</div>', groups: undefined]
str = str.replace(result[0],result[1])
console.log(str)
	// 控制台输出:  <div>我叫name</div>

多次替换:

let str = '<div>我叫{{name}},今年{{age}}岁</div>'
let reg = /{{([a-zA-Z]+)}}/    //+匹配前面的子表达式一次或多次

//第一次匹配
let result = reg.exec(str)
console.log(result)  //'{{name}}', 'name', index: 7,
str = str.replace(result[0],result[1])
console.log(str)  // <div>我叫name,今年{{age}}岁</div>

//第二次匹配
let result2 = reg.exec(str)
console.log(result2)  //'{{age}}', 'age', index: 14,
str = str.replace(result2[0],result2[1])
console.log(str)  // <div>我叫name,今年age岁</div>

//第三次匹配
let result3 = reg.exec(str)
console.log(result3)  // null

多次替换(循环版):

let str = '<div>我叫{{ name }},今年{{ age }}岁</div>'
let reg = /{{\s*([a-zA-Z]+)\s*}}/
//+匹配前面的子表达式一次或多次, \s*匹配空格0次或多次。
while (reg.exec(str)) {
    let result = reg.exec(str)
    str = str.replace(result[0],result[1])
}
console.log(str)
	// 控制台输出: <div>我叫name,今年age岁</div>

有了前面的铺垫之后,接下来就是创建对象,然将对象中的值替换到str中。

let data = {'name':'liuyu','age':22}
let str = '<div>我叫{{ name }},今年{{ age }}岁</div>'
let reg = /{{\s*([a-zA-Z]+)\s*}}/

while (reg.exec(str)) {
    let result = reg.exec(str)
    // result[1]为通过分组获取的内容,也就是name和age
    str = str.replace(result[0],data[result[1]])
}
console.log(str)
	// 控制台输出:<div>我叫liuyu,今年22岁</div>
  • 这里是通过 object[key]的方式来获取value的,不可以通过object.key也就是 data.result[1]来获取,可能实际取值时是这样的 data.“name”。

6.4.1 手动实现简易模板引擎

步骤:

  • 一、导入模板引擎文件

    <!--引入自定义的模板引擎文件-->
    <script src="....路径/myTemplate-web.js"></script>
    
  • 二、书写模板

    <script type="text/html" id="tpl">
    <div>我叫{{ name }},今年{{ age }}岁,来自{{ addr }}</div>
    </script>
    
  • 三、定义数据

    let data = {'name':'liuyu','age':22,'addr':'上海市徐汇区'}
    
  • 四、调用template函数

    art-template模板引擎中,我们导入template-web.js文件的同时,会在全局产生一个template函数.

    现在是自己手动实现,所以并没有该函数,接下来就是手写简易的template函数

    // template-web.js文件
    function template(id,data) {
    let str = $('#'+id).html()
    let reg = /{{\s*([a-zA-Z]+)\s*}}/
    while (reg.exec(str)) {
    let result = reg.exec(str)
    str = str.replace(result[0],data[result[1]])
    }
    return str
    }
    
    <!--调用-->
    <script>
    let data = {'name':'liuyu','age':22,'addr':'上海市徐汇区'}
    let htmlStr = template('tpl',data)
    console.log(htmlStr)
    // <div>我叫liuyu,今年22岁,来自上海市徐汇区</div>
    </script>
    
  • 五、渲染标签

    将调用函数返回的htmlStr渲染到HTML代码结构内部即可,源码略。

七、XMLHttpRequest

什么是XMLHttpRequest:

  • XMLHttpRequest简称xhr,是浏览器提供的JavaScript对象,通过它,可以请求服务器上的数据资源,之前使用的jQuery中的Ajax函数,就是基于xhr对象封装出来的。

我们可以使用 . g e t ( ) 、 .get()、 .get().post() 、$.ajax()发起请求,也可以直接使用xhr对象发起ajax请求。

7.1 使用xhr发起GET请求

步骤:

  • 创建xhr对象。
  • 调用xhr.open()函数,指定请求方式与URL地址
  • 调用xhr.send()函数,发起Ajax请求。
  • 监听xhr.onreadystatechange事件。

代码示例:

//创建XHR对象
let xhr = new XMLHttpRequest()
//调用open函数,指定请求方式与URL地址
xhr.open('GET','http://www.escook.cn:3006/api/getbooks')
//调用send函数,发起Ajax请求
xhr.send()

//监听onreadystatechange事件
xhr.onreadystatechange = function () {
    //监听xhr对象的请求状态readyState;以及服务器的响应状态status。
    if (xhr.readyState === 4 && xhr.status === 200) {   //固定写法
        //打印服务器响应回来的数据
        console.log(xhr.responseText)
    }
}
//控制台输出;  {"status":200,"msg":"获取图书列表成功","data":[{...},{...},....]}

注意:

  • xhr.status与返回的"status":200 ,这两个status不是同一个。
  • 前者是HTTP请求的状态
  • 后者是响应成功后,服务器返回的数据。

7.1.1 xhr对象的readyState属性

XMLHttpRequest对象的readyState属性,用来表示当前Ajax请求所处的状态,每个Ajax请求必须处于以下状态中的一个:

状态描述
0unsentxhr对象已创建,但未调用open方法
1openedopen方法已经被调用
2headers_receivedsend方法已经被调用,响应头也已经被接收
3loading数据接收中,此时response属性中已经包含部分数据
4doneAjax请求完成,这意味着数据传输已经彻底完成失败

7.1.2 发起带参数的GET请求

使用xhr对象发起带参数的GET请求时,只需要在调用xhr.open()期间,为URL地址指定参数即可:

xhr.open('get','http://www.escook.cn:3006/api/getbooks?id=1')
//控制台输出:{"status":200,"msg":"获取图书列表成功","data":[{"id":1,....}]}

URL地址中,?后面表示查询参数,多个参数之间用&隔开,以这种形式,可以将想要发送给服务器的数据添加到URL中,而这种形式,又叫做查询字符串

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

7.2 使用xhr发起POST请求

步骤:

  • 创建xhr对象
  • 调用xhr.open()函数
  • 设置Content-Type属性
  • 调用xhr.send()函数,同时指定要发送的数据
  • 监听xhr.conreadystatechange事件

代码示例:

/*1.创建xhr对象*/
let xhr = new XMLHttpRequest()
/*2.调用open函数,指定请求方式以及URL*/
xhr.open('post','http://www.escook.cn:3006/api/addbook')
/*3.设置Content-Type属性(必须的操作,固定写法)*/
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
/*4.调用send函数,同时将数据以查询字符串的形式,提交给服务器*/
xhr.send('bookname=刑法学讲义&author=张三&publisher=法内狂徒')
/*5.监听onreadystatechange事件*/
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 & xhr.status === 200) {
        console.log(xhr.responseText)
    }
}
/*
控制台输出:
	{"status":201,"msg":"添加图书成功"}
*/

7.3 URL编码

现象:

  • 例如在发起带参数的GET请求时,会出现下列情况:

    //重复代码省略了
    xhr.open('GET','http://www.escook.cn:3006/api/getbooks?id=1&bookname=西游记')
    /*
    请求的URL地址:
     http://www.escook.cn:3006/api/getbooks?id=1&bookname=%E8%A5%BF%E6%B8%B8%E8%AE%B0
    */
    

可以发现,<西游记>这三个中文汉字,变成了%E8%A5%BF%E6%B8%B8%E8%AE%B0,这种编码叫做URL编码

什么是URL编码:

  • URL地址中,只允许出现英文相关的字母、标点符号、数字、因此,在URL地址中不允许出现中文字符。
  • 如果URL中需要包含中文这样的字符,则必须对中文字符进行编码(转义)

URL编码的原则

  • 使用英文字符去表示非英文字符。
  • 中文编码后,三个%的内容为一个中文字符,如%E8%A5%BF = 西

7.3.1 如何对URL进行编解码

浏览器提供了URL编码与解码的API,分别是:

  • encodeURL() 编码的函数
  • decodeURL() 解码的函数

代码示例:

解码

let str = '%E8%A5%BF%E6%B8%B8%E8%AE%B0'
console.log(decodeURI(str))
// 西游记

编码

let str = '西游记'
console.log(encodeURI(str))
// %E8%A5%BF%E6%B8%B8%E8%AE%B0

由于浏览器会自动对URL地址进行编码操作,因为大多数情况下,程序员不需要关系URL地址的编码与解码操作。

八、数据交换格式

什么是数据交换格式:

  • 数据交换格式,就是服务端客户端之间的数据传输与交换的格式
  • 前端领域,经常提及的两种数据交换格式分别是XMLJSON,其中XML用的非常少,所以主要以JSON为主。

什么是XML

  • XML的英文全称是EXtensible Markup Language,即可扩展标记语言,因此XML和HTML类似,也是一种标记语言。

XML和HTML虽然都是标记语言,但是两者之间没有任何的关系。

XML和HTML的区别:

  • HTML被设计用来描述网页上的内容,是网页内容的载体。(传输网页内容)
  • XML被设计用来传输和储存数据,是数据的载体。(传输数据)

XML的缺点:

  • XML格式臃肿(很多标签),和数据无关的代码多,体积大,传输效率低。
  • 在JavaScript中解析XML比较麻烦。

什么是JSON

  • JSON的英文全称是JavaScript Object Notation,即“JavaScript对象表示法”,简单来说JSON就是JavaScript对象和数组的字符串表示法,它使用文本表示一个JS对象或数组的信息,因此JSON的本质,就是字符串。

作用

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

JSON格式:

  • 对象的key必须使用英文的双引号,value的数据类型可以使数字、字符串、布尔值、null、数组、对象6种类型。
  • 数组中的数据类型可以是数组、字符串、布尔值、null、数组、对象6中类型。

练习:找出不符合JSON格式的地方

let jsonStr = {
name:"liuyu",
'age':22,
"sex":'男',
"addr":undefined,
"hobby":["吃饭","睡觉",'刷抖音'],
say:function () {}
}  
  • name和age属性没有使用双引号
  • sex的值,没有使用双引号
  • addr的值,undefined不符合数字、字符串、布尔值、null、数组、对象6种类型
  • hobby中有元素没有使用双引号。
  • say没有使用双引号,且函数不属于6中类型。

8.1 序列化与反序列化

什么是序列化:

  • 数据对象转换为字符串的过程,叫做序列化
  • 字符串转换为数据对象的过程,叫做反序列化

序列化方法:

函数名:

  • JSON.stringift()
    

代码示例:

let obj = {'name':'liuyu','age':22}
let jsonStr = JSON.stringify(obj)
console.log(jsonStr,typeof jsonStr)
//{"name":"liuyu","age":22} string

反序列化方法:

函数名:

  • JSON.parse()
    

代码示例:

let jsonStr = '{"name":"liuyu","age":22}'
let obj = JSON.parse(jsonStr)
console.log(obj,typeof obj)
// {name: 'liuyu', age: 22} 'object'

九、XHR level2的新特性

旧版XMLHttpRequest的缺点

  • 只支持文本数据的传输,无法用来读取和上传文件
  • 传送和接收数据时,没有进度信息,只能提示有没有完成。

XMLHttpRequest level2的新功能:

  • 可以设置HTTP请求的时限。
  • 可以使用FormData对象管理表单数据
  • 可以上传文件
  • 可以获得数据传输的进度信息

9.1 设置HTTP请求时限

有时,Ajax操作很耗时,而且无法预知要花多少时间,如果网速很慢,用户可能要等很久,新版本的XMLHttpRequest对象,增加了timeout属性,可以设置HTTP请求的时限。

xhr.timeout = 3000

上面的语句,将最长等待时间设为3000毫秒,过了这个时限,就自动停止HTTP请求,与之配套的还有一个timeout事件,用来指定回调函数

xhr.ontimeout = function(event) {
alert('请求超时!')
}

代码书写位置:

  • 超时时间以及处理超时时间的函数,可以直接在实例化xhr对象之后,就定义。

9.2 FormData对象管理表单数据

Ajax操作往往用来提交表单数据,为了方便表单处理,HTML5新增了一个FormData对象,可以秒你表单操作。

代码示例:

let fd = new FormData()
// 追加数据
fd.append('uname','liuyu')
fd.append('upwd','123456')

let xhr = new XMLHttpRequest()
xhr.open('post','http://www.escook.cn:3006/api/formdata')
// 新版本可以把setRequestHeader省略,因为改成默认值了。
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))
}
}
/*{message: '测试FormData表单提交成功!', data: {…}}*/

实际应用中,FormData对象可以帮助我们一次性获得所有表单的数据,类似于form表单的serialize函数

<form action="" id="form1">
姓名: <input type="text">
密码: <input type="text">
<button type="submit">提交</button>
</form>
let form = document.querySelector('#form1')
form.addEventListener('submit',function (e) {
e.preventDefault()
/*根据form表单创建FormData对象,会自动的将表单数据填充到FormData对象中*/
let fd = new FormData(form)
let xhr = new XMLHttpRequest()
xhr.open('post','http://www.escook.cn:3006/api/formdata')
xhr.send(fd)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 & xhr.status === 200) {
   console.log(JSON.parse(xhr.responseText))
}
}
})
// {message: '测试FormData表单提交成功!', data: {…}}

最终效果:

  • 表单中输入的信息,会被FormData对象所接收,后续提交到服务端,返回反序列化的数据。

9.3 上传文件

实现步骤:

  • 定义UI结构
  • 验证是否选择了文件
  • 向FormData中追加文件
  • 使用xhr发起上传文件的请求
  • 监听onreadystatechange事件

一、定义UI结构

<input type="file" id="file1" style="display: inline-block">
<button id="btnUpload" class="btn btn-success" style="display: inline-block">上传文件</button>
<br>
<img src="" alt="" id="img" width="400">

二、验证是否选择了文件

let btnUpload = document.querySelector('#btnUpload')
btnUpload.addEventListener('click',function () {
let files = document.querySelector('#file1').files
if (files.length <=0 ) {
return alert('请选择文件')
}
/*后续业务逻辑*/
})

三、向FormData中追加文件

//*后续业务逻辑,上述多余代码略*/
let fd = new FormData()
fd.append('avatar',files[0])
// 这里的avatar为后端接口的参数属性,files[0]为值,该值为选中上传的文件。

四、使用xhr发起上传文件的请求

//*后续业务逻辑,上述多余代码略*/
let fd = new FormData()
fd.append('avatar',files[0])
/*使用xhr发起上传文件的请求*/
let xhr = new XMLHttpRequest()
xhr.open('post','http://www.escook.cn:3006/api/upload/avatar')
xhr.send(fd)

五、

let btnUpload = document.querySelector('#btnUpload')
btnUpload.addEventListener('click',function () {
let files = document.querySelector('#file1').files
if (files.length <=0 ) {
return alert('请选择文件')
}
let fd = new FormData()
fd.append('avatar',files[0])

let xhr = new XMLHttpRequest()
xhr.open('post','http://www.escook.cn:3006/api/upload/avatar')
xhr.send(fd)

xhr.onreadystatechange = function () {
if (xhr.readyState === 4 & xhr.status === 200) {
   // 将返回的数据反序列化
   let data = JSON.parse(xhr.responseText)
   // 判断服务器返回的状态码,在确认POST请求没问题的同时,再确认图片是否上传成功。
   if (data.status !== 200) {
       return alert('上传失败')
   }
   // 利用DOM操作,将上传到服务器的图片渲染到页面上
   let img = document.querySelector('#img')
   img.src = 'http://www.escook.cn:3006'+ data.url
}
}
})

附:服务端返回的数据

  • {message: '上传文件成功!', status: 200, url: '/uploads/1660416057298_082f06d5dbff41419db77926641cc4a9.jpg'}
    

9.4 计算文件上传进度–后续更新

十、axios – 后续更新

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿煜酱~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值