在HTML中提供了表单提交的功能,咱们能够经过表单把数据从前台提交到后台,经过设置submit
事件,能够为按钮或其它元素一样能够触发表单提交的事件javascript
<body> <form id="user" action="02.php" method="post"> 帐号:<input type="text" value="请输入帐号"><br> 密码:<input type="password" value="请输入密码"><br> 重置:<input type="reset" value="重置"><br> <button id="btn">提交</button> </form> <script> let btnObj=document.getElementById("btn"), formObj=document.forms[0];//forms表示获取页面中全部的表单,返回一个数组 btn.onclick=function(){ formObj.submit(); console.log("提交成功") } </script> </body>
经过表单提交数据不须要依赖js引擎,浏览器自己就能够实现,但这样也会产生一个问题,用户提交数据信息的时候须要刷新整个页面,这就大大下降了用户体验php
Ajax的诞生解决了上述的问题,Ajax表明的是:Asynchronous Javascript And XML
,也就是异步,javascript和XML,Ajax不是一门编程语言,而是一种用于建立更好更快交互性更强的web应用技术,能够经过少许的浏览器和服务器之间的数据交互实现页面的部分刷新,在使用了Ajax技术后,用户的请求数据是间接经过Ajax引擎来发出的,而不是直接经过浏览器来发出,服务器返回的数据也是传回到Ajax引擎html
Ajax虽然在使用时的优势显而易见,但同时缺点也是存在的:前端
咱们能够对比一下传统的提交方式和使用Ajax技术的提交方式的不一样:java
Ajax的精华部分就是它的Ajax引擎,那么什么是Ajax引擎呢?就是XMLHttpRequest
对象,除了已经入土的IE5,其它现代浏览器都支持该对象,同时它也是一个JavaScript对象,它负责发出请求和接收响应web
在IE5和IE6中Ajax引擎是ActiveXRequest
,因此咱们可能须要作一下兼容性ajax
<script> function createAjax(){ var xhr; window.XMLHttpRequest?xhr=new XMLHttpRequest():xhr=new ActiveXObject("Msxml2.XMLHTTP"); return xhr; } </script>
function createRequst() { return window.XMLHttpRequest ? new window.XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP'); } console.log(createRequst());//在IE7-中返回的是一个object对象
咱们在控制台中打印出XMLHttpRequest
对象的方法和属性编程
咱们找几个比较主要的说一下,json
onloadstart 请求开始时触发的事件onloadend 请求结束时触发的事件api
ontimeout 请求超时时触发的事件
onreadystatechange 请求状态改变时触发的事件
readyState 请求状态
status 服务器返回的状态码
timeout 设置请求的时间,当请求的时间超过咱们设置的时间时,自动将请求注销掉
withCredentials 是否要求证书(例如12306)
responseText 在非XML格式下返回的数据
var xhr=createRequst();//封装的函数 xhr.open("GET","data.php",true); //若是是使用GET方式提交数据那么须要在URl后添加?而后把键值对之间以&符号连接拼接在?后面 //若是是使用POST获取数据,那么咱们要将参数传递到send中,键值对与键值对之间使用&符号拼接
在创建链接时,咱们要调用open方法,open方法有三个参数
open(type,url,isAsync),这几个参数分别表明:
这里咱们须要了解一下一样是请求后台数据,GET和POST的区别,若是只是列举简单的区别:
可是,在看到一篇大神的文章以后,以上的区别通通显的不重要了,咱们来开始学习怎样高逼格的说明这俩者的不一样
咱们都知道,不管是GET仍是POST请求,都是创建在HTTP协议之上的,而HTTP协议又是创建在TCP协议之上的,也就是HTTP协议规定的数据在万维网中传输的的规定,那么说白了GET和POST只是两种不一样的TCP连接,只是因为浏览器的规定设置致使了有一些差别,在本质上并无什么区别,实际上真正的差异在于,GET和POST在传输数据的过程当中,GET会产生一个TCP数据包,而POST会产生两个数据包
浏览器在发送GET请求时是将http header和data一块儿发出,服务响应200,而在发送POST请求时。浏览器会先发送http header,服务器响应100 continue,再发送data,服务器响应200,也就是说,GET方式提交数据是一次性的,而POST第一次先告诉服务器我要请求数据,第二次再将数据提交过去
因此这里因为POST是两次请求,耗时会增长,这也是雅虎优化建议中为何提出,咱们尽可能将页面中使用POST的方式改成GET来提高响应速度,可是,在网络性能好的状况下,使用GET和POST的时间差异几乎没有,在网络较差的状况下,两次打包会大大提高数据的完整性,并且不是全部浏览器都是两次打包的,Firefox就是使用一次打包
至于安全性方面,GET和POST一样不安全,经过POST加密的数据会被抓包工具截取,若是有要求使用加密一样须要经过加密方式进行加密
继续上面的栗子:
xhr.onreadystatechange=function(){ if(xhr.readyState===4){ //判断请求状态是不是已经完成 if(xhr.status>=200&&xhr.status<=300||xhr.status===304){ //判断服务器是否返回成功200,304 console.log(xhr.responseText); //接收xhr的数据 } } }
这里有一些须要咱们了解的
readyState
请求的状态码0: 表示请求未初始化,也就是没有创建open请求
1: 表示创建open()请求,正在发送请求
2: 表示请求已经接收,open()方法已经完成,已经收到所有响应内容
3: 表示请求处理中,正在解析响应内容
4: 表示请求已经完成,响应也已经就绪,也就是说响应内容已经解析完成,能够在客户端进行调用了
status
服务器返回的状态码,有几种常见的类型:
以1开头表明请求已经接受
以2开头表明请求已经成功
以3开头表明网页重定向,常见的几个:
以4开头表明请求错误 ,检查是否是url输入错误或者是后台人员接口给错了
以5开头表明服务器错误,须要后台人员配合进行调试
浏览器有默认的缓存机制,在第一次向服务器请求数据的时候,在发现页面中存在文件请求时会发出请求,并将这些请求缓存在本地,在第二次向服务器发送请求的时候,在请求头中发送一个Last-Modified
,它包含了上一次从服务器得到数据的日期。若是服务器判断从这个日期起服务器中数据没有进行修改,那么直接返回304
的状态,再也不从新发送数据,此时用户仅仅是获取了304
状态码,不会再次请求数据,而是直接从本地读取缓存
避免服务器返回304
最简单的方法就是直接在url地址中添加一个随机数的时间戳,例如:/Home/GetMusic?c_time=时间戳
在前端有个设置能够解决读取缓存的方法,设置meta标签的属性
<meta http-equiv="prgma" content="no-cache">
或者设置Ajax默认禁止缓存
var xhr=createRequst();//封装的函数 xhr.open("GET","data.php",true); xhr.onreadystatechange=()=>{ if(xhr.readyState===4){ if(xhr.status>=200&&xhr.status<=300||xhr.status===304){ console.log(xhr.responseText); } } } xhr.setRequsetHeader("Content-type":"application/x-www-form-urlcoded"); xhr.setRequsetHeader("Canhe-Control","no-cache");//阻止浏览器读取缓存 xhr.send();
若是使用jQuery进行开发,能够在在jQuery中设置Ajax的cache属性,该值默认为ture,设置为false即表明禁止浏览器缓存
在使用POST
发送页面的时候须要注意的是,要发送请求头,格式以下:
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); //设置请求头格式为仿照form表单提交
在发送Ajax请求时默认发送的请求头格式为文本格式,若是用来提交表单数据会报错,因此须要咱们认为的设置请求头格式,以上的代码的做用就是告诉服务器咱们发送的是form表单格式的数据
xhr.send();
这里咱们注意一个问题,关于send()
的位置问题,以前咱们一直把send()
放在监听事件状态onreadystatechange
以前,这样写在默认的异步状态下是没有问题的,但若是是在同步的状态下看,是会报错的,这里涉及到了执行顺序的问题,咱们是须要先准备好怎么样接收数据,而后才能对数据进行接收
json
是一种键值对模式的字符串,其中的键和值都是用双引号进行包裹,因为json
的轻量和便于使用,如今大部分公司使用的都是json
格式的数据
var str={"name":"Tom","age",18}//若是值是数字的话能够不使用双引号包裹
具体关于json
的定义能够参考JSON
在浏览器中为咱们提供了两种json数据格式转化的方法
json
数据格式转化为对象格式,须要重点注意的是在使用该方法时,必须是键值对使用双引号包裹,因此外层的须要是单引号json
格式的字符串这里其实还有一个方法能够解析json格式的字符串,eval
,这个方法比较特殊,W3C对它的介绍是,很强大的一个功能,可是运用的不多,这是由于eval
方法自己会自动执行字符串中所包含的逻辑语句,因此若是咱们使用该方法直接接收没有验证过的返回信息就会很容易遭到攻击,例如:
var str={"name":alert("弹出")}; var o=eval(str);
在执行上述代码时会自动弹出alert提示框,由于在使用eval
时已经执行了字符串内部的逻辑代码,因此不推荐使用该方法
在JQuery中对Ajax方法进行了封装,JQuery的Ajax封装是很是强大的,在其它框架中也大量借鉴了该方法,下面咱们来看一下在JQuery中是如何使用Ajax的
比较简单方法:
$(function(){ $("#btn").on("click",function(){ $.post("02.php",{name:"Tom",age:18},function(data){ console.log(data) },'json') }) }) //相对应的还有GET和getJSON等其它方法,基本套路都同样
下面的是咱们常常用到的,一样也是在其余框架中被参考的方法$.ajax
$.ajax({ type:"get",//设置请求的方式 url:"02.php",//设置请求的地址 data:{name:"tom",age:18},//设置发送的参数 dataType:"json",//请求的数据类型,自动转化为对象 async:true,//设置是否异步,默认为true cache:false,//设置浏览器是否缓存该页面,默认为true timeout:300,//设置请求超时时间 beforeSend:function(xhr){//请求开始前回调该函数 console.log("请求开始前"); //return false 若是在请求开始前直接return等于阻止了ajax请求 }, success:function(result,status){//请求成功时回调该函数,,result表明返回的数据,status为状态描述 console.log("请求成功了"); }, error:function(xhr,status,error){//请求失败时回调该函数,xhr为 XMLHttpRequest对象,status为错误信息,error表示捕获的错误对象 console.log("请求失败,"+xhr.status); } }) //以上只是咱们经常使用的一些方法和参数,JQuery中还封装了不少其它的方法和参数,包括上面提到的$.get和$.post等方法都是该方法的变种
咱们简单的模拟一下jQuery对Ajax的封装
function formatData(data) {//格式化数据的方法 var ret = []; for (var key in data) { ret.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key])); //有时候传入的多是汉字,因此须要利用encodeURIComponent转化成编码格式 //encode方法废弃了,如今提供的是encodeURLConponent方法 } ret.push(("=" + new Date().getTime())); //添加时间戳,防止获取缓存的数据(http状态码304) return ret.join("&"); } function createRequst() {//建立Ajax引擎对象 return XMLHttpRequest() ? new XMLHttpRequest() : new ActiveXObject("Msxml2.XMLHTTP") } function jsonp(option) { //jsonP方法 //判断用户传入的参数 if (!option || !option.url || !option.callback) { console.error("参数异常"); return; } //若是用户参数传入正确,建立script标签,添加到head标签中 var scriptElement = document.createElement("script"), headElement = document.getElementsByTagName("head")[0]; headElement.appendChild(scriptElement); //注意jsonp建立的函数是全局函数,为了防止代码污染,起特定的名字 var fnName = ("jsonp_" + Math.random()).replace(".", ""); option.data[option.callback] = fnName; //给用户传入的向后台发送的data数据中添加一个option.callback属性,并赋值为咱们自定义建立的函数名,让后台接收咱们的函数名,后台才能返回给咱们数据 window[fnName] = function (data) { if (option.timeout) {//用户若是设置了超时时间,在接收到数据后将计时器清除 window.clearTimeout(scriptElem.timer); } delete window[fnName];//删除全局函数 headElement.removeChild(scriptElem);//删除建立的script标签 option.success && option.success(data); } if (option.timeout) {//若是用户设置了超时时间,设置计时器,在用户设置的超时时间到达后若是该函数执行,说明超时,那么执行其中的代码 scriptElement.timeout = window.setTimeout(function () { delete global[fnName]; headElem.removeChild(scriptElem); option.fail && option.fail({"message": "请求超时"}); }, option.timeout) } scriptElem.src = option.url + '?' + formatData(option.data);//执行跨域请求,跨域的时候不须要其它参数,因此data中的数据就是callback和它的值 } $.extend({ AjaxSetting: {//设置用户默认输入的各项参数 url: "", type: "GET", dataType: "json", async: true, success: null,//成功后执行的函数 fail: null,//失败后执行的的函数 contentType: "application/x-www-form-urlencoded;charset=UTF-8"//模拟表单提交的请求头 }, ajax: function (url, option) {//在JQuery中使用的两个参数,这里仿照JQuery写法 var context, xhr; if (typeof url === "object") {//判断url类型,若是用户直接传入一个对象,说明没有单独传入URL地址,那么咱们执行下面的代码 option = url; url = null; } else {//若是用户单独传入了url地址,那么咱们执行下面的代码 option.url = url; url = undefined; } if (!option || !option.url || !option.success) {//判断用户是否输入option,option中的url和success是咱们设置为默认必须的,因此在用户传入时就进行判断 console.error("参数传入异常"); return; //若是用户传入的参数异常,那么咱们弹出异常,而且直接返回 } // 经过JQuery中的extend方法获取默认设置 $.extend($.AjaxSetting, context); // 经过JQuery中的extend方法获取用户设置,覆盖或新增默认的设置 $.extend(option, context); if (context.dataType.toLowerCase() === "jsonp") {//判断用户是否要求跨域 jsonp(context);//若是是的话执行jsonp的方法 } else {//不然的话执行Ajax函数 xhr = createRequst();//经过createReqyst方法执行函数 xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { var data = context.dataType.toLowerCase() == "json" ? JSON.parse(xhr.responseText) : xhr.responseText; //判断用户需求什么格式的数据,若是是json的数据,将请求来的数据进行转换 context.success && context.success(data, context, xhr); //判断用户是否输入了success方法,若是有的话执行success方法,若是咱们直接执行的 话,因为上面设置了success为null,用户若是没有设定会报错 } } else { context.fail && context.fail({"message": "请求超时."}) } } if (context.type.toUpperCase() == "GET") { xhr.open("GET", context.url + "?" + formatData(context.data), context.async); //利用formatData方法格式化数据 xhr.send(); } else { xhr.open("POST", context.url, context.async); xhr.setRequestHeader("contentType", context.contentType) xhr.send(formatData(context.data)); } } } })
Ajax轮询和长链接都是为了实现页面的实时刷新,可是须要注意,若是在不是必要的状况下尽可能不要使用这两种方法,大量的不间断的数据请求对前台和后台都是很是有压力的,若是要实现页面的实时刷新,建议使用WebSocket
客户端定时向服务器发送Ajax请求,服务器在返回数据后断开链接,轮询可以实现页面的实时刷新,可是缺点也很明显,定时的发送数据大部分是没有意义的,并且占用额外的服务器资源
function Ajax(){ setInterval(function(){ var xhr=new XMLHttpRequest(); xhr.open("open","url",true); xhr.onreadystatechange=function(){ if(xhr.readyState===4){ if(xhr.status>=200&&xhr.status<=300||xhr.status==304){ console.log(xhr.responseText) } } }; xhr.setRequestHeader("Content-type","qpplication/x-www-form-urlencoded"); },5000) }
代码实际上很简单,就是调用计时器,每过一段时间向后台发送一次请求
因此为了可以使页面及时刷新,而且改善轮询存在的弊端,咱们通常使用的是长轮询的方法,也就是ling-poling
$(function(){ (function longPolling(){ $.ajax({ url:"01.php", type:"get", datatype:"json", timeout:5000, error:function(xml,status,eror){ //能够选择报请求超时的错误,不过通常不这么作 longPolling(); }, success:function(data,status,xhr){ console.log(data); //若是有数据返回,那么将数据添加到页面 longPolling(); } }) })() })
Ajax长轮询须要后台写相应的代码来配合完成,实际代码也就是不管成功或者失败都再次调用自身来向后台发起请求
什么是模板引擎,说白了就是在字符串中有几个变量待定,咱们能够用来配合Ajax
请求返回的数据来动态填充页面内容,例如:
//这里用到的是attTemplate模板引擎 <script type="text/template" id='template01'> //为了防止script对内容进行解析,将script的type属性变为非javascript便可,改成template主要是为了增长代码的可读性 <tr> <td><%=name%></td> //<%= %>表明输出值 <td><%=skill%></td>//在这里<%= %>中的skill表明的就是咱们设定的待定的变量 <td><%=wife%></td> <td> <% for(var i=0;i<friends.length;i++){ %> // <% %>里的内容表明的是输出的逻辑语句 <a><%=friends[i]%></a> <% }%> </td> </tr> </script>
咱们能够在写一个完整的:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="js/template-native.js"></script>//导入模板引擎js文件 <script type="text/template" id="template"> <% for(var i = 0;i < posts.length; i++) {%> <% var post = posts[i]; %> <% if(!post.expert){ %> <span>post is null</span> <% } else { %> <a href="#"><%= post.expert %> at <%= post.time %></a> <% } %> <% } %> </script> </head> <body> <script> var data = { "posts": [{ "expert": "content 1", "time": "yesterday" },{ "expert": "content 2", "time": "today" },{ "expert": "content 3", "time": "tomorrow" },{ "expert": "", "time": "eee" }] }; var str=template('template',data);//artTemplate模板引擎的使用就是调用template方法,传入模板的id和变量的值 console.log(str) </script> </body> </html>
封装方法主要是使用了string的方法和正则进行匹配,下面写一下大概的封装
function template(templateStr,data){//传入两个值,templateStr表明模板的字符串,data表明传入的变量对象 var reg = /<%=\s*([^%>]+\S)\s*%>/,//定义正则匹配<%= %> result; while(result=reg.exec(templateStr)){//根据正则的exec方法的属性若是没有符合的返回null来肯定循坏中止的条件 var matchString=result[0],//根据exec方法返回的是<%= %>这部分的字符串 matchWord=result[1];//根据exec方法返回的是<%= %>中的字符,也就是咱们传入的data中的键 templateStr=templateStr.replace(matchString,data[matchWord]); //调用字符串的rplace方法,符合的目标进行替换 } return templateStr; }
出于安全性的考量,现代全部的浏览器都遵循同源策略,同源策略不容许Ajax获取其它网站的数据,咱们经过某种方式来获取其它网页的数据的方式就是跨域
简单来讲就是在两个网页域名,端口,协议任意不一样的状况下,A网页没法获取B网页的数据,举一个例子来讲明:
http://www.example.com/dir/page.html http://www.example.com/dir2/other.html //同源 http://example.com/dir/other.html //不一样源,域名不一样 http://v2.www.example.com/dir/other.html //不一样源,域名不一样 http://www.example.com:81/dir/other.html //不一样源,端口不一样
注意,同一域名下的不一样服务器获取数据也是跨域,例如两台电脑的IP地址不一样,A不能直接获取B电脑服务器的数据
若是是非同源那么会受到如下限制:
具体的内容能够参考:同源策略
什么是JSONP,不少人会把jsonp和Ajax搞混,或者认为jsonp是Ajax提供的跨域方法,但特别须要注意,这二者是相互独立的,这里引用一段知乎的回答
jsonp的本质能够认为是因为src
属性不受同源策略的限制,能够获取其它域的数据,利用src
可以跨域获取数据的特性来实现咱们从其它的网站获取数据,前提是必须在该网站配合下
实际在以前的不少操做中咱们已经使用到了src可以跨域的这个特性,例如咱们在img中插入一张图片,能够直接把图片的地址复制到src中
<img src="https://www.baidu.com/img/bd_logo1.png">
因此须要注意,src这个属性其实是等同于发送了一个GET形式的Ajax请求,包括href
,因此咱们在页面中尽可能不要写空的href
和src
属性,能够减少服务器的压力
下面咱们来举一个jsonP跨域的例子:
<script type="text/javascript" src='http://192.168.18.38/2016-8-22/coding/cross/05.jsonP.php?callBack=sayhi'> </script> <script type="text/javascript"> function sayhi(data) { //参数data就是咱们要获取的跨域的数据,在拿到以后能够配合模板引擎在页面中放置 console.log(data); //sayHi方法会自执行,由于在后台传送数据时会用括号将callback的值包裹起来 } //后台在接收到`callBack`后,获取到`sayhi`的名称,返回`sayhi('("+data+")')`,因此咱们接收的函数的参数就是咱们要获取的数据 </script>
在JQ中为咱们封装了跨域的方法,有两种方式:
1.$.ajax()方法 <script type="text/javascript"> $(function(){ $.ajax({ url:"http://api.map.baidu.com/telematics/v3/weather?location=北京 &output=json&ak=tq0Kt0NFYheTCj5mszast0drkIqqVTNn", dataType:"jsonp",//只须要将dataType设置为jsonp便可 success:function(data){ console.log(data) } }) }) </script> 2.getJson方式 <script type="text/javascript"> $.getJson('http://csdn.net/blog/data.java?callback=?',function(data){console.log(data)//处理返回的数据}); </script>
在一个window的存活周期下,窗口载入的全部页面都共享一个window.name
属性,每一个页面都对它有可读可写的权限,即便window.location
重载也不会有变化
window.name
的格式为字符串,更多的时候咱们使用json的格式来进行书写,window.name
存在字符串的限制,最大的大小为2M
下面咱们来举一个栗子:
a.html
<body> <input type="button" id="btn" value="点击"> <script> window.name="这是a网页设置的"; document.getElementById("btn").onclick=function(){ window.location.href="b.html" } </script> </body>
b.html
<body> <script> console.log(window.name);//"这是a网页设置的" </script> </body>
这个方法和window.name
同样,须要是同一个页面打开的窗口
咱们直接来举一个栗子:
//数据端发送 <script> //弹出一个新窗口 var myPopup = window.open(domain + '/windowPostMessageListener.html','myWindow'); //周期性的发送消息 setInterval(function(){ var message = 'Hello! The time is: ' + (new Date().getTime()); var domain = 'http://scriptandstyle.com'; console.log('blog.local: sending message: ' + message); myPopup.postMessage(message,domain); //postMessage有两个参数1.message表明的是要发送的数据 // 2.domain表明的是要发送数据到哪一个地址 },6000); </script> //数据端接收 <script> //响应事件 window.addEventListener('message',function(event) { if(event.origin !== 'http://davidwalsh.name') return; console.log('message received: ' + event.data,event); event.source.postMessage('holla back youngin!',event.origin); },false); //须要注意的是1.须要对浏览器进行判断,众所周知IE使用的是attachEvent //这里注意message是postMessage的接收事件,其中有三个参数1.source,表明消息源,也就是发送消息的窗口 //2.origin,表明消息源的url,包含协议,端口,域名,用来验 证数据源 //3.data,发送方发送过来的数据 </script>
这个方法是实际工做中最经常使用的,咱们若是有须要跨域请求数据,通常会让后台配合请求到数据而后在返回给前台使用
这个后面再说