在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>
這個方法是實際工做中最經常使用的,咱們若是有須要跨域請求數據,通常會讓後臺配合請求到數據而後在返回給前臺使用
這個後面再說