复习 - ajax
复习呢有一个很直观的感受,就是以前学的东西,萌懂半懂的,这一来全部都清楚了,你以前以为你学的并不好但是复习一次把以前的案例一做,居然能够自己独立完成,知识点看着掌握的还不错。
1.
两天时间就把整个ajax复习完了,一天目前还暂时做不到哈哈,确实还是有很多案例都要思考一会,直接从案例下手吧,一个图书管理的案例,在这个案例里面吧就是用三个接口来获取图书,增加图书,删除图书,在js方面没多大问题,在html方面,还让我多熟悉了下vscode快速编程bootstrap,直接bs3,form-inline类名可以让每个表单项为行内块元素,table-hover可以增加表格的每一行悬停效果。
获取图书列表:封装为一个函数,通过ajax发起get请求,然后把拿到的数据通过foreach循环出来。
增加图书列表:也是发起一起请求,然后要重新获取一下列表
<!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>Document</title> <link rel="stylesheet" href="./lib/bootstrap.css"> <style> .panel-body { text-align: center; } .input-group { width: 30%; margin: 0 10px; } .panel { width: 90%; margin: 0 auto; } .table { width: 90%; margin: 15px auto 0; } </style> </head> <body> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">添加新图书</h3> </div> <!-- 1.添加了一个类名form-inline 可使里面的表单元素变成行内块元素 --> <div class="panel-body form-inline"> <div class="input-group "> <div class="input-group-addon">书名</div> <input type="text" class="form-control bookname" id="exampleInputAmount" placeholder="请输入书名"> </div> <div class="input-group"> <div class="input-group-addon">作者</div> <input type="text" class="form-control author" id="exampleInputAmount" placeholder="请输入作者"> </div> <div class="input-group"> <div class="input-group-addon">出版社</div> <input type="text" class="form-control publisher" id="exampleInputAmount" placeholder="请输入出版社"> </div> <button type="button" class="btn btn-primary">添加</button> </div> </div> <!-- 2.table-borderred可以为每一个表单元素增加边框 table-hover 添加鼠标滑过表单的悬停状态 --> <table class="table table-bordered table-hover"> <thead> <tr> <th>id</th> <th>书名</th> <th>作者</th> <th>出版社</th> <th>操作</th> </tr> </thead> <tbody> </tbody> </table> <script src="./lib/jquery.js"></script> <script> /* $.get('http://www.liulongbin.top:3006/api/getbooks', res => { // console.log(res); if (res.status !== 200) return console.log(获取数据失败); let htmlStr = '' let str = '' res.data.forEach(item => { htmlStr = `<tr> <td>${item.id}</td> <td>${item.bookname}</td> <td>${item.author}</td> <td>${item.publisher}</td> <td><a href="javascript:;">删除</a></td> </tr>` str += htmlStr }) document.querySelector('tbody').innerHTML = str }) */ // 3.上面我自己的做的方法固然可以但是这里既然是用的jq那就用jq的方法来实现 function getBook() { $.ajax({ method : 'get', url : 'http://www.liulongbin.top:3006/api/getbooks', success : res => { if (res.status !== 200) return alert('获取数据失败') // jq的循环方法 // 注意这里的i不能省略 let arr = [] $.each(res.data, (i, item) => { arr.push(`<tr> <td>${item.id}</td> <td>${item.bookname}</td> <td>${item.author}</td> <td>${item.publisher}</td> <td><a href="javascript:;" data-id="${item.id}" class="del">删除</a></td> </tr>`) }) $('tbody').empty().append(arr.join('')) }}) } getBook() // 删除图书模块 function delBook() { // 4.删除图书也要用到一个接口 // 4.1注意这里的jq的事件委托,在后代选择器这里,不管是写id还是class还是标签都不再需要$符号直接引号写上来即可 $('tbody').on('click', '.del' , function() { // 这里点谁就会触发谁,用到了事件委托,说明现在每个a标签上也有点击事件了他们就是事件的调用者 // attr这个方法可以设置可以获取属性的值,删除用removeAttr let id = $(this).attr('data-id') console.log(id); $.get('http://www.liulongbin.top:3006/api/delbook', {id : id}, res => { if (res.status !== 200) return alert('删除失败') // 删除成功刷新一下表格 getBook() }) }) } delBook() // 添加图书模块 function addBook() { $('.btn').on('click', function() { $.post('http://www.liulongbin.top:3006/api/addbook',{ bookname : $('.bookname').val(), author : $('.author').val(), publisher : $('.publisher').val() }, res => { console.log(res); if (res.status !== 201) return alert(res.msg) getBook() return alert(res.msg) }) }) } addBook() </script> </body> </html>
2.
第二个是一个聊天机器人的案例,以前也说过这些案例,只是这次来做感想又深入了一步,这个案例分为了三步,先是把自己发的消息能够渲染到聊天界面,然后添加一个resetui重置右侧滚动条的函数,可以让聊天界面跟着我们的刚发的消息走,然后把获取机器人回复消息封装为一个函数,具体里面是通过我们发的消息为一个参数然后去获取请求,会给你传回来一个参数,同时可以把这个参数以机器人回复的姿态渲染到聊天界面,最后就是语音播放功能,把机器人的消息作为一个参数封装一个函数,html增加一个audio标签,src就为我们这次请求的src,要加上网页的一个根路径哦
<!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-1.12.4.min.js"></script> <script type="text/javascript" src="js/jquery-ui.min.js"></script> <script type="text/javascript" src="js/jquery.mousewheel.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;"> </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" /> <input type="button" value="发 送" class="input_sub" /> </div> </div> <audio src="" autoplay></audio> <!-- <script type="text/javascript" src="./js/scroll.js"></script> --> <!-- <script> $(function(){ // 初始化右侧滚动条 // 这个方法定义在scroll.js中 resetui() }) </script> --> <!-- 1.下面两个jq的js都是为了配合最后一个可以用resetui函数来初始化右侧滚动条让滚动条跟着屏幕内容走 --> <script src="./js/jquery-1.12.4.min.js"></script> <script src="./js/jquery-ui.min.js"></script> <script src="./js/jquery.mousewheel.js"></script> <script src="./js/scroll.js"></script> <script> // 先完成点击发送添加消息功能 let content = '' $('.input_sub').on('click', function() { content = $('.input_txt').val() if (content.trim() == '') return $('.talk_list').append(`<li class="right_word"> <img src="img/person02.png" /> <span>${content}</span> </li>` ) $('.input_txt').val('') resetui() getBoot(content) }) // 机器人回复 // 2.注意这里机器人的回复是怎么做到的 是再点击后调用的这个函数 function getBoot(text) { $.get('http://www.liulongbin.top:3006/api/robot',{spoken : text},res => { console.log(res); if (res.message == 'success') { $('.talk_list').append(`<li class="left_word"> <img src="img/person01.png" /> <span>${res.data.info.text}</span> </li>` ) } resetui() getSpeaker(res.data.info.text) }) } // 语音回复 // 3.注意这里也是需要被别人调用函数 function getSpeaker(text) { $.ajax({ method : 'get', url : 'http://www.liulongbin.top:3006/api/synthesize', data : {text : text}, success : res => { if (res.status !== 200) return alert('获取语音失败') $('audio').attr('src', res.voiceUrl) } }) } // 最后回车发送消息 $('.input_txt').on('keyup', (e) => { // console.log(e.keyCode); if (e.keyCode == 13) { $('.input_sub').trigger('click') } }) </script> </body> </html>
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>Document</title> </head> <body> <div></div> <!-- 2.模板 --> <script type="text/html" id="model"> <div>我的名字:{{name}}</div> <div>我的年龄:{{age}}</div> <div>我的性别:{{sex}}</div> <div>我的家庭住址:{{address}}</div> </script> <script> // 3.封装函数 function template(id, data) { let str = document.getElementById(id).innerHTML // console.log(str); let reg = /{{([a-zA-Z]+)}}/ let result = reg.exec(str) while (result !== null) { str = str.replace(result[0], data[result[1]]) result = reg.exec(str) } return str } </script> <script> // 1.定义数据 let data = { name : '王五', age : 18, sex : '男', address : '重庆市江北区' } // 4.调用 let htmlStr = template('model', data) // 5.渲染 document.querySelector('div').innerHTML = htmlStr </script> </body> </html>
以下就是一个完整的模板引擎使用案例,在这个案例中我的几个错误点:一个是如果你标准语法使用了循环后,在你的每一个循环的项里面有一个字符串你想给转换成数组,还记得你下面要获取数据吗,那么就在这里做一个循环,对每一条data数据里面的字符串,push进数组,错误点二就是标准语法里面关于图片这一点,首先要知道凡事用到了标准语法那就必须{{}}包起来,因为你图片还有一个根路径嘛,所以也要一起包着进去。
<!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>Document</title> <link rel="stylesheet" href="./assets/news.css" /> <script src="./lib/jquery.js"></script> <script src="./lib/template-web.js"></script> </head> <body> <div id="news-list"> </div> </body> <script type="text/html" id="model"> {{each data}} <div class="news-item"> <!-- <img class="thumb" src="http://www.liulongbin.top:3006'+${{$value.img}}+'" alt="" /> --> <!-- 2.错误点二,凡是用到变量的地方都需要标准语法 这里需要把整个都包起来 --> <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 {{tags}} | arrFormat}} <span>{{$value}}</span> {{/each}} --> <!-- 1.错误点一 这里我想的太复杂了,不用过滤器来做,但是我认为这样做应该可以只是不知道哪一步搞错了,这里有个更简便的方法 在获取数据的时候就可以将每一个tags有字符串改为数组 --> <span>{{$value.tags[0]}}</span> <span>{{$value.tags[1]}}</span> <span>{{$value.tags[2]}}</span> </div> <div class="footer"> <div> <span>{{$value.source}}</span> <span>{{$value.time | timeFormat}}</span> </div> <span>评论数:{{$value.cmtcount}}</span> </div> </div> </div> {{/each}} </script> <script> $.ajax({ type : 'GET', url : 'http://www.liulongbin.top:3006/api/news', success : res => { if (res.status !== 200) return alert('获取新闻列表失败') console.log(res); for (let i = 0; i < res.data.length; i++) { // 1.1注意 这一换成数组后需要将当前整个赋值给她 res.data[i].tags = res.data[i].tags.split(',') } let htmlStr = template('model', res) $('#news-list').append(htmlStr) } }) template.defaults.imports.timeFormat = function(value) { let date = new Date(value) let y = date.getFullYear() let m = addZero(date.getMonth() + 1) let d = addZero(date.getDate()) let hh = addZero(date.getHours()) let mm = addZero(date.getMinutes()) let ss = addZero(date.getSeconds()) return `${y}-${m}-${d} ${hh}:${mm}:${ss}` } // 补零函数 function addZero(n) { return n < 10? '0' + n : n } </script> </html>
4.
然后封装自己的ajax函数,用到原生js的xhr方法,在封装里面去判断你是get还是post请求通过toUpperCase,包括xhr level2的一些新功能,设置HTTP时限,xhr.timout对应还有一个事件ontimeout,formdata表单管理可以拿来模拟表单数据,同样也可以拿来获取表单数据,第三个是可以上传文件了,关键步骤在于获取到上传文件的表单元素后面跟一个.files就会得到一个上传的文件的数组,跟第四个新特性组合起来就是有进度显示,通过一个事件xhr.upload.onprogress里面有三个e的属性但是要注意这个时间必须写到open和send函数之前,然后就是jq里面的ajaxstart和ajaxend两个事件。
然后就是jsonp,其原理就是通过script标签不受同源策略限制而通过src发起的服务器请求,把回到函数、参数都加进去,在jq里面jsonp通过ajax方法来做,datatype改为jsonp。
一个案例来综合展示jsonp、防抖和全局缓存,首先我们的关键字需要作为一个参数去获取建议列表,然后再定义模板这里循环res,里面就写一个标签,因为里面的value使我们数组里面的每一项,直接取第一个,然后会根据数组的长度自动给你多少个标签,渲染到页面上
防抖就是会定义一个函数里面是一个定时器去执行我们获取建议列表的函数,当你一触发这个事件,首先会清除这个定时器然后在执行定时器函数,你想想我如果设置个时间50ms,那么我们打字的速度,肯定是比这个还快的,所以你一直在输入他也一直在清楚定时器,定时器就一直没有执行,当你停下来了,这个时候正常执行定时器,获取到我们的建议列表,下面会有两个图来详细展示一下区别,有防抖和没有防抖的。
然后就是全局缓存,就是我们的输入一个apple再来一个mac,这个时候是两个单词吧,那我把mac一删,难道又要去请求一次apple,那不就请求三次了吗,我只请求两次完成这个操作可以吗,那就要用到全局缓存,定义一个全局空对象,在我们渲染html那里,空对象的属性名为搜索的关键字=为我们的res,然后当我们搜索的时候先去判断搜索的关键字通过一个for in循环看看对象里面有没有这个关键字,如果有先执行缓存里面的res,就不再去请求服务器了。
最后是一个节流策略,就是我们限制我们的触发次数,跟防抖还是有本质区别的,通过一个节流阀,定义一个timer为null,进入这个事件给她定义为多少ms的定时器,进入这个事件先去检测,timer是不是为空,如果不为空就return只有当为null才会去执行
<!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>Document</title> <style> img { position: absolute; left: 0; top: 0; } html, body { width: 100%; height: 100%; } </style> </head> <body> <img src="./angel.gif" alt=""> <script> let timer = null document.body.addEventListener('mousemove', (e) => { if (timer) return timer = setTimeout(() => { document.querySelector('img').style.left = e.pageX + 'px' document.querySelector('img').style.top = e.pageY+ 'px' console.log(11); timer = null // console.log(e.pageX); }, 15); }) </script> </body> </html>