ajax(同源與跨域)
7. http協議
協議是指計算機通信網絡中兩臺計算機之間進行通信所必須共同遵守的規定或規則 HTTP協議,即超文本傳輸協議(Hypertext transfer protocol)。是一種詳細規定了瀏覽器和服務器之間互相通信的規則,HTTP協議分為請求 和響應 兩個部分組成。
7.1. 請求與請求報文
get請求的請求報文詳解
//--------------------------請求行-------------------------------- // GET 請求方式 // /day02/01.php?username=hucc&password=123456 請求路徑+參數(註意點) // HTTP/1.1 HTTP的版本號 GET /day02/01.php?username=hucc&password=123456 HTTP/1.1 //--------------------------請求頭-------------------------------- // Host:主機地址 Host: www.study.com // HTTP1.1版本默認開啟,建立過連接後,TCP連接不會斷開,下次連接可以繼續使用(底層,不用管) Connection: keep-alive //chrome瀏覽器自己增加的,不用管 Upgrade-Insecure-Requests: 1 //瀏覽器的代理字符串(版本信息) User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36 //瀏覽器端可以接受的類型。 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,`*/*`;q=0.8 //從哪個頁面發出的請求 Referer: http://www.study.com/day02/01-login.html //檢查瀏覽器支持的壓縮方式 Accept-Encoding: gzip, deflate, sdch //瀏覽器支持的語言,優先中文。 Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 //----------------------------請求體------------------------------------- //get請求沒有請求體,但是參數會拼接到請求行中
POST請求的請求報文
//-----------------------請求行--------------------------------------------- POST /day02/01.php HTTP/1.1 //-----------------------請求頭-------------------------------------------- Host: www.study.com Connection: keep-alive //傳遞的參數的長度。 Content-Length: 29 Cache-Control: max-age=0 Origin: http://www.study.com Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36 //內容類型:表單數據,如果是post請求,必須指定這個屬性。 Content-Type: application/x-www-form-urlencoded Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,`*/*`;q=0.8 Referer: http://www.study.com/day02/01-login.html Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 //------------------------請求體------------------------------------------ username=hucc&password=123456
GET請求與POST請求的對比
- GET請求沒有請求體,因為GET請求的參數拼接到地址欄中了
- POST請求有請求體,就是傳遞的參數
- POST請求需要指定content-type屬性。
7.2. 響應與響應報文
//---------------------狀態行(響應行)-------------------------------
//HTTP/1.1 HTTP版本
//200 響應的狀態
//200表示成功
//303 返回另一個url //304表示讀緩存 //404表示找不到資源 //500表示服務端錯誤 HTTP/1.1 200 OK //----------------------響應頭----------------------------------------------- Date: Thu, 22 Jun 2017 16:51:22 GMT Server: Apache/2.4.23 (Win32) OpenSSL/1.0.2j PHP/5.4.45 X-Powered-By: PHP/5.4.45 Content-Length: 18 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive //內容類型,告訴瀏覽器該如何解析響應結果 Content-Type: text/html;charset=utf-8 //-----------------------響應體------------------------------------------------ 用戶登錄成功
通常來說,我們不會用抓包工具來查看請求和響應,太麻煩了,可以直接使用谷歌瀏覽器來查看請求報文和響應報文。
谷歌瀏覽器會對報文進行一定的格式化,看起來雖然不是原生的報文,但是使用起來更加的方便簡潔。
8. AJAX
即 Asynchronous [e‘s??kr?n?s] Javascript And XML, AJAX 不是一門的新的語言,而是對現有技術的綜合利用。 本質是在HTTP協議的基礎上以異步的方式與服務器進行通信。
8.1. 同步與異步
同步和異步概念:
同步: 指的就是事情要一件一件做。等做完前一件才能做後一件任務
異步: 不受當前任務的影響,兩件事情同時進行,做一件事情時,不影響另一件事情的進行。
編程中:異步程序代碼執行時不會阻塞其它程序代碼執行,從而提升整體執行效率。
網頁異步應用:
- 驗證你的用戶名是否已經存在(一邊輸入,一邊獲取你的信息,和後臺比對)。
- 百度搜索提示,及相關內容展示(一邊輸入,一邊找出了你可能要的內容)。
- 新浪微博評論(異步加載)。
XMLHttpRequest可以以異步方式的處理程序。
8.2. XMLHttpRequest
瀏覽器內置對象,用於在後臺與服務器通信(交換數據) , 由此我們便可實現對網頁的部分更新,而不是刷新整個頁面。這個請求是異步,即在往服務器發送請求時,並不會阻礙程序的運行,瀏覽器會繼續渲染後續的結構。
8.2.1. 發送get請求
XMLHttpRequest以異步的方式發送HTTP請求,因此在發送請求時,一樣需要遵循HTTP協議。
//使用XMLHttpRequest發送get請求的步驟
//1. 創建一個XMLHttpRequest對象
var xhr = new XMLHttpRequest;//構造函數沒有參數的情況,括號可以省略
//2. 設置請求行
//第一個參數:請求方式 get/post
//第二個參數:請求的地址 需要在url後面拼上參數列表
xhr.open("get", "08.php?name=hucc");
//3. 設置請求頭
//瀏覽器會給我們默認添加基本的請求頭,get請求時無需設置
//4. 設置請求體
//get請求的請求體為空,因為參數列表拼接到url後面了
xhr.send(null);
- get請求,設置請求行時,需要把參數列表拼接到url後面
- get請求不用設置請求頭
- get請求的請求體為null
8.2.2. 發送post請求
var xhr = new XMLHttpRequest;
//1. 設置請求行 post請求的參數列表在請求體中
xhr.open("post", "09.php");
//2. 設置請求頭, post請求必須設置content-type,不然後端無法獲取到數據
xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
//3. 設置請求體
xhr.send("name=hucc&age=18");
-
post請求,設置請求行時,參數列表不能拼接到url後面
-
post必須設置請求頭中的content-type為application/x-www-form-urlencoded
-
post請求需要將參數列表設置到請求體中.
8.2.3. 獲取響應
HTTP響應分為3個部分,狀態行、響應頭、響應體。
//給xhr註冊一個onreadystatechange事件,當xhr的狀態發生狀態發生改變時,會觸發這個事件。
xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
//1. 獲取狀態行
console.log("狀態行:"+xhr.status);
//2. 獲取響應頭
console.log("所有的相應頭:"+xhr.getAllResponseHeaders());
console.log("指定相應頭:"+xhr.getResponseHeader("content-type"));
//3. 獲取響應體
console.log(xhr.responseText);
}
}
readyState
readyState:記錄了XMLHttpRequest對象的當前狀態
//0:請求未初始化(還沒有調用 open())。
//1:請求已經建立,但是還沒有發送(還沒有調用 send())。
//2:請求已發送,正在處理中
//3:請求在處理中;通常響應中已有部分數據可用了,但是服務器還沒有完成響應的生成。
//4:響應已完成;您可以獲取並使用服務器的響應了。(我們只需要關註狀態4即可)
8.3. 數據交互
瀏覽器端只是負責用戶的交互和數據的收集以及展示,真正的數據都是存儲在服務器端的。我們現在通過ajax的確可以返回一些簡單的數據(一個字符串),但是在實際開發過程中,肯定會會設計到大量的復雜類型的數據傳輸,比如數組、對象等,但是每個編程語言的語法都不一樣。因此我們會采用通過的數據交換格式(XML、JSON)來進行數據的交互。
8.3.1. XML
什麽是XML
- XML 指可擴展標記語言(EXtensible Markup Language)
- XML 是一種標記語言,很類似 HTML
- XML 的設計宗旨是傳輸數據,而非顯示數據
- XML 標簽沒有被預定義。您需要自行定義標簽。
語法規範
- 第一行必須是版本信息
- 必須有一個根元素(有且僅有一個)
- 標簽不可有空格、不可以數字或.開頭、大小寫敏感
- 不可交叉嵌套,都是雙標簽,如果是單標簽,必須閉合
- 屬性雙引號(瀏覽器自動修正成雙引號了)
- 特殊符號要使用實體
- 註釋和HTML一樣
<students>
<student>
<name>張三</name>
<age>18</age>
<gender>男</gender>
<desc>路人甲</desc>
</student>
<student>
<name>李四</name>
<age>20</age>
<gender>男</gender>
<desc>路人乙</desc>
</student>
</students>
php獲取xml文件的內容
//註意,如果需要返回xml數據,需要把content-type改成text/xml,不然瀏覽器以text/html進行解析。
header(‘content-type:text/xml;charset=utf-8‘);
//用於獲取文件的內容
//參數:文件的路徑
$result = file_get_contents("data.xml");
echo $result;
html解析xml
//獲取服務端返回的xml數據,需要使用xhr.responseXML,這是一個document對象,可以使用DOM中的方法查找元素。
var data = xhr.responseXML;
//獲取所有的學生
var students = data.querySelectorAll("student");
缺點:雖然可以描述和傳輸復雜數據,但是其解析過於復雜並且體積較大,所以實現開發已經很少使用了。
8.3.2. JSON數據
JSON(JavaScript Object Notation, JS 對象標記) 是一種輕量級的數據交換格式。它基於 ECMAScript 規範的一個子集,采用完全獨立於編程語言的文本格式來存儲和表示數據。
- 數據在名稱/值對中
- 數據由逗號分隔(最後一個健/值對不能帶逗號)
- 花括號保存對象,方括號保存數組
- 鍵使用雙引號
var obj = {a: ‘Hello‘, b: ‘World‘}; //這是一個對象,註意鍵名也是可以使用引號包裹的
var json = ‘{"a": "Hello", "b": "World"}‘; //這是一個 JSON 字符串,本質是一個字符串
JSON數據在不同語言進行傳輸時,類型為字符串,不同的語言各自也都對應有解析方法,需要解析完成後才能讀取
8.3.2.1. php處理json
- php關聯數組==> json
// php的關聯數組
$obj = array(
"a"=>"hello",
"b"=>"world",
"name"=>"胡聰聰"
);
//json字符串
$json = json_encode($obj);
echo $json;
- json===>php對象
$json = ‘{"a": "Hello", "b": "World"}‘;//json字符串
//第一個參數:json字符串
//第二個參數:
//false,將json轉換成對象(默認)
//true:將對象轉換成數組(推薦)
$obj = json_decode($json,true);
echo $obj[‘a‘];
//通過json文件獲取到的內容就是一個json字符串。
$data = file_get_contents("data.json");
//將json轉換成數組
$result = json_decode($data, true);
print_r($result);
8.3.2.2. JS處理json
- JS對象 ==> JSON字符串 JSON.stringify(obj)
//obj是一個js對象 var obj = {a: ‘Hello‘, b: ‘World‘} //result就變成了一個json字符串了 var result = JSON.stringify(obj);// ‘{"a": "Hello", "b": "World"}‘
- JSON字符串 ==> JS對象 JSON.parse(obj)
//json是一個json字符串 var json = ‘{"a": "Hello", "b": "World"}‘; //obj就變成了一個js對象 var obj = JSON.parse(json);// {a: ‘Hello‘, b: ‘World‘}
使用json進行數據傳輸
思考:
- js有一個對象,如何發送到php後臺
- php中有一個對象,如何發送到前臺。
8.3.3. eval方法
eval會執行參數,參數是一個字符串,把這個字符串當成代碼來執行。eval() 函數可計算某個字符串,並執行其中的的 JavaScript 代碼。eval的參數是一個字符串,這個字符串是需要執行的表達式或者語句。
console.log(eval("{}"));//undefined,因為{}被當成了代碼塊
console.log(eval("({})"));//obj,因為{}用()引起來了。
//使用這個方法,也可以將一個json字符串轉換成js對象。
var json = ‘{"name":"zs", "age":18, "sex":"男"}‘;
var obj = eval("(" + json + ")");
console.log(obj);
註意:eval函數的功能非常的強大,但是實際使用的情況並不多。
- eval形式的代碼難以閱讀
- eval形式的代碼無法打斷點,因為本質還是還是一個字符串
- 在瀏覽器端執行任意的 JavaScript會帶來潛在的安全風險,惡意的JavaScript代碼可能會破壞應用
8.4. 兼容性處理
8.5. 封裝ajax工具函數
每次發送ajax請求,其實步驟都是一樣的,重復了大量代碼,我們完全可以封裝成一個工具函數。
//1. 創建xhr對象
//2. 設置請求行
//3. 設置請求頭
//3. 設置請求體
//4. 監聽響應狀態
//5. 獲取響應內容
8.5.1. 參數提取
參數名 | 參數類型 | 描述 | 傳值 | 默認值 |
---|---|---|---|---|
type | string | 請求方式 | get/post | get |
url | string | 請求地址 | 接口地址 | 當前頁地址 |
async | boolean | 是否異步 | true/fase | true |
data | object | 請求數據 | {key:value,key:value} |
|
success | function | 響應成功時調用 | - | - |
error | function | 響應失敗時調用 | - | - |
dataType | 服務器返回的格式 | json/xml/text(默認) | dataType:"json" |
8.5.2. 參數檢測
//如果options參數沒有傳遞,直接返回。
if (!options || typeof options !== "object") {
return;
}
//處理默認參數
//如果參數不是post,那就默認為get
var type = options.type == "post" ? "post" : "get";
//如果沒有傳url,那就傳當前地址
var url = options.url || location.href;
//如果參數不是false,那就默認是true,發異步請求
var async = options.async == false ? false : true;
8.5.3. 完整版
var $ = {
ajax: function (options) {
//如果options參數沒有傳遞,直接返回。
if (!options || typeof options !== "object") {
return;
}
//處理默認參數
//如果參數不是post,那就默認為get
var type = options.type == "post" ? "post" : "get";
//如果沒有傳url,那就傳當前地址
var url = options.url || location.pathname;
//如果參數不是false,那就默認是true,發異步請求
var async = options.async == false ? false : true;
var params = this.getParams(options.data);
var xhr = new XMLHttpRequest();
//設置請求行
if (type == "get") {
url = url + "?" + params;
}
xhr.open(type, url, async);
//設置請求頭
if (type == "post") {
xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
}
//設置請求參數
xhr.send(params);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
/*根據響應頭的content-type屬性指定方法接收到的內容*/
var contentType = xhr.getResponseHeader(‘content-type‘);
var data = null;
if (contentType.indexOf(‘json‘) > -1) {
data = JSON.parse(xhr.responseText);
} else if (contentType.indexOf(‘xml‘) > -1) {
data = xhr.responseXML;
} else {
data = xhr.responseText;
}
/*執行成功函數*/
options.success && options.success(data);
} else {
options.error && options.error(xhr.responseText);
}
}
}
},
getParams: function (obj) {
//將obj對象轉換成參數
//將對象轉換成參數列表
if (!obj) {
return null;
}
var arr = [];
for (var k in obj) {
arr.push(k + "=" + obj[k]);
}
return arr.join("&");
}
}
8.6. jQuery中的ajax方法
jQuery為我們提供了更強大的Ajax封裝
8.6.1. $.ajax
參數列表
參數名稱 | 描述 | 取值 | 示例 |
---|---|---|---|
url | 接口地址 | url:"02.php" | |
type | 請求方式 | get/post | type:"get" |
timeout | 超時時間 | 單位秒 | timeout:5000 |
dataType | 服務器返回的格式 | json/xml/text(默認) | dataType:"json" |
data | 發送的請求數據 | 對象 | data:{name:"zs", age:18} |
beforeSend | 調用前的回調函數 | function(){} | beforeSend:function(){ alert(1) } |
success | 成功的回調函數 | function (data) {} | success:function (data) {} |
error | 失敗的回調函數 | function (error) {} | error:function(data) {} |
complete | 完成後的回調函數 | function () {} | complete:function () {} |
使用示例:
$.ajax({
type:"get",//請求類型
url:"02.php",//請求地址
data:{name:"zs", age:18},//請求數據
dataType:"json",//希望接受的數據類型
timeout:5000,//設置超時時間
beforeSend:function () {
//alert("發送前調用");
},
success:function (data) {
//alert("成功時調用");
console.log(data);
},
error:function (error) {
//alert("失敗時調用");
console.log(error);
},
complete:function () {
//alert("請求完成時調用");
}
});
8.6.2. 其他api
// 第一個參數:url // 第二個參數:[data] // 第三個參數:callback // 第四個:dataType // $.post("01-post.php", {uname:"hucc",upass:"123456"}, function (info) { // console.log(info); // }, "json"); //如果發送get請求,不需要指定data,這種寫法會更加簡潔。 // $.get("01-get.php",function (info) { // console.log(info); // }, "json");//$.post(options);只發送post請求
//$.get(options);//只發送get請求
//$.getJson(url,callback);//獲取服務器端的json文件
//$.getScript(url,callback);//載入服務器端的js文件,(動態加載js文件)
//$.load(url);//載入一個服務器端的html頁面。
8.6.3. 接口化開發
請求地址即所謂的接口,通常我們所說的接口化開發,其實是指一個接口對應一個功能, 並且嚴格約束了請求參數和響應結果的格式,這樣前後端在開發過程中,可以減少不必要的討論, 從而並行開發,可以極大的提升開發效率,另外一個好處,當網站進行改版後,服務端接口進行調整時,並不影響到前端的功能。
8.6.3.1. 獲取短信驗證碼接口
需求文檔
//總需求:點擊發送按鈕,向服務端發送請求
//需求1:格式校驗
//1. 手機號碼不能為空 如果為空提示"手機號不能為空"
//2. 手機號碼格式必須正確, 提示"請輸入正確的手機號碼"
//需求2:點擊發送時,按鈕顯示為"發送中",並且不能重復提交請求
//需求3:根據不同的響應結果,進行響應。
//1. 如果接口調用成功
//如果響應代碼為100,倒計時
//如果響應代碼為101,提示手機號重復
//2. 如果接口調用失敗,告訴用戶"服務器繁忙,請稍候再試"
接口文檔
//接口說明:獲取短信驗證碼
//接口地址:getCode.php
//請求方式:get
//接口傳參:mobile 手機號
//返回類型 json
//接口返回:{"code":"101","msg":"手機號碼存在", "mobile":"18511249258"}
//code 當前業務邏輯的處理成功失敗的標識 100:成功 101:手機號碼存在
//msg 當前系統返回給前端提示
//mobile 當前的手機號碼
8.6.3.2. 註冊接口
表單序列化
jquery提供了一個serialize()
方法序列化表單,說白就是將表單中帶有name屬性的所有參數拼成一個格式為name=value&name1=value1
這樣的字符串。方便我們獲取表單的數據。
//serialize將表單參數序列化成一個字符串。必須指定name屬性
//name=hucc&pass=123456&repass=123456&mobile=18511249258&code=1234
$(‘form‘).serialize();
//serializeArray將表單序列化成一個數組,必須指定name屬性
//[{name:"name", value:"hucc"},{name:"pass", value:"123456"},{name:"repass", value:"123456"},{name:"mobile", value:"18511241111"}, {name:"code", value:"1234"}]
$(‘form‘).serializeArray();
jquery的ajax方法,data參數能夠直接識別表單序列化的數據data:$(‘form‘).serializeArray()
$.post({
url:"register.php",
data:$(‘form‘).serializeArray(),
dataType:‘json‘,
success:function (info) {
console.log(info);
}
});
需求文檔
form表單一個id,下面有name屬性自動生成字符串
data: $("#ajaxForm").serialize()
//註冊功能
//總需求:點擊註冊按鈕,向服務端發送請求
//需求1:表單校驗
//1.1 用戶名不能為空,否則提示"請輸入用戶名"
//1.2 密碼不能為空,否則提示"請輸入密碼"
//1.3 確認密碼必須與密碼一直,否則提示"確認密碼與密碼不一致"
//1.4 手機號碼不能為空,否則提示"請輸入手機號碼";
//1.5 手機號碼格式必須正確,否則提示"手機號格式錯誤"
//1.6 短信驗證碼必須是4位的數字,否則提示"驗證碼格式錯誤"
//需求2:點擊註冊按鈕時,按鈕顯示為"註冊中...",並且不能重復提交請求
//需求3:根據不同響應結果,處理響應
//3.1 接口調用成功
//100 提示用戶註冊成功,3s後跳轉到首頁
//101 提示用戶"用戶名hucc已經存在"
//102 提示用戶"驗證碼錯誤"
//3.1 接口調用失敗,提示"服務器繁忙,請稍後再試",恢復按鈕的值
接口文檔
//接口說明:註冊
//接口地址:register.php
//請求方式:post
//接口傳參:name:用戶名 pass:密碼 code:驗證碼 mobile:手機號
//返回類型 json
//接口返回:{"code":"100","msg":"註冊成功","name":"huccc"}
//code 當前業務邏輯的處理成功失敗的標識 100:成功 101:用戶存在 102:驗證碼錯誤
//msg 當前系統返回給前端提示
//name: 註冊的用戶名
9. 模板引擎
是為了使用戶界面與業務數據(內容)分離而產生的,它可以生成特定格式的文檔,用於網站的模板引擎就會生成一個標準的HTML文檔。
9.1. 為什麽要使用模板引擎
我們通過ajax獲取到數據後,需要把數據渲染到頁面,在學習模板引擎前,我們的做法是大量的拼接字符串,對於結構簡單的頁面,這麽做還行,但是如果頁面結構很復雜,使用拼串的話代碼可閱讀性非常的差,而且非常容易出錯,後期代碼維護也是相當的麻煩。
9.2. 常見的模板引擎
BaiduTemplate:http://tangram.baidu.com/BaiduTemplate/ velocity.js:https://github.com/shepherdwind/velocity.js/ ArtTemplate:https://github.com/aui/artTemplate
artTemplate是使用最廣泛,效率最高的模板引擎,需要大家掌握。
9.3. artTemplate的使用
9.3.1. artTemplate入門
1.引入模板引擎的js文件
<script src="template-web.js"></script>
2.準備模板
<!--
指定了type為text/html後,這一段script標簽並不會解析,也不會顯示。
-->
<script type="text/html" id="myTmp">
<p>姓名:隔壁老王</p>
<p>年齡:18</p>
<p>技能:查水表</p>
<p>描述:年輕力氣壯</p>
</script>
3.準備數據
//3. 準備數據,數據是後臺獲取的,可以隨時變化
var json = {
userName:"隔壁老王",
age:18,
skill:"查水表",
desc:"年輕氣壯"
}
4.將模板與數據進行綁定
//第一個參數:模板的id
//第二個參數:數據
//返回值:根據模板生成的字符串。
var html = template("myTmp", json);
console.log(html);
5.修改模板
<script type="text/html" id="myTmp">
<p>姓名:{{userName}}</p>
<p>年齡:{{age}}</p>
<p>技能:{{skill}}</p>
<p>描述:{{desc}}</p>
</script>
6.將數據顯示到頁面
var div = document.querySelector("div");
div.innerHTML = html;
9.3.2. artTemplate語法
if語法
{{if gender=‘男‘}}
<div class="man">
{{else}}
<div class="woman">
{{/if}}
each語法
<!--
1. {{each data}} 可以通過$value 和 $index獲取值和下標
2. {{each data v i}} 自己指定值為v,下標為i
-->
{{each data v i}}
<li>
<a href="{{v.url}}">
<img src="{{v.src}}" >
<p>{{v.content}}</p>
</a>
</li>
{{/each}}
//如果返回的數據是個數組,必須使用對象進行包裹,因為在{{}}中只寫書寫對象的屬性。
var html = template("navTmp", {data:info});
10. 瀑布流案例
10.1. 封裝jQuery瀑布流插件
//特點分析:
//1. 跟以前將的瀑布流不一樣的是,這次的瀑布流固定版心為1200px
//2. 瀑布流固定擺放5列,每一列的寬度固定為232px。
//思路分析:
//1. 計算每一列之間的縫隙
//2. 初始化一個數組,用戶存儲每一列的高度 [0,0,0,0,0]
//3. 查找數組的最小列,每次都把圖片定位到最小列的位置
//4. 更新數組最小列的高度(加上定位過來的圖片的高度)
代碼參考:
$.fn.waterfall = function () {
var $box = $(this);
var $item = $box.children();
var boxWidth = $box.width();//父盒子的寬度
var itemWidth = 232;//每個盒子固定寬度為232
var columns = 5;//固定擺放5列
var gap = (boxWidth - columns * itemWidth) / (columns - 1);//縫隙的寬度 10
var arr = [0, 0, 0, 0, 0]; //初始化數組
$item.each(function () {
//查找最小列
var min = arr[0];
var minIndex = 0;
for (var i = 0; i < arr.length; i++) {
if (min > arr[i]) {
min = arr[i];
minIndex = i;
}
}
//設置位置
$(this).css({
left: minIndex * (itemWidth + gap),
top: min
});
//更新數組
arr[minIndex] = min + $(this).outerHeight() + gap;
});
}
10.2. 瀑布流完整版
//需求分析:
//1. 頁面剛開始,沒有任何一張圖片。因此需要從通過ajax獲取圖片
//2. 使用模版引擎將獲取到的數據渲染到頁面
//3. 因為圖片路徑是從服務端獲取的,加載需要時間,需要等待圖片加載完成後才能使用瀑布流進行布局。
//4. 給window註冊scroll事件,當觸底時,需要動態的加載圖片。
//5. 加載時,顯示加載中的提示信息,並且要求不能重復發送ajax請求
//6. 當服務端返回圖片數量為0時,提示用戶沒有更多數據。
接口文檔
//接口說明:瀑布流分頁數據
//接口地址:data.php
//請求方式:get
//接口參數:page 當前是第幾頁 pageSize 當前頁需要顯示多少條
//返回類型 json
//返回數據:
{
page: 2,
items:[
{path: "./images/1.jpg",text:‘這是描述信息‘},
{path: "./images/2.jpg",text:‘這是描述信息‘}
{path: "./images/2.jpg",text:‘這是描述信息‘}
]
}
//page 下一頁的頁碼
//items 返回當前頁的數據
//path 圖片地址
//text 文字
11. 綜合案例
11.1. 數據庫初始化
/*
1. phpstudy-->MYSQL管理器-->MySQL-Front-->根據下圖連接信息連接數據庫
2. 選擇test數據庫,點擊sql編輯器,根據下列sql語句,生成數據。
*/
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`sex` varchar(20) NOT NULL DEFAULT ‘男‘,
`academy` varchar(400) NOT NULL DEFAULT ‘前端與移動開發學院‘,
`introduce` varchar(1000) NOT NULL DEFAULT ‘暫無‘,
`createTime` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES (‘1‘, ‘張三01‘, ‘男‘, ‘前端與移動開發學院‘, ‘暫無‘, ‘2017-05-26 16:39:40‘);
INSERT INTO `user` VALUES (‘2‘, ‘張三02‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘3‘, ‘張三03‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘4‘, ‘張三04‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘5‘, ‘張三05‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘6‘, ‘張三06‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘7‘, ‘張三07‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘8‘, ‘張三08‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘9‘, ‘張三09‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘10‘, ‘張三09‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘11‘, ‘張三10‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘12‘, ‘張三11‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘13‘, ‘張三12‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘14‘, ‘張三13‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
INSERT INTO `user` VALUES (‘15‘, ‘張三14‘, ‘男‘, ‘前端與移動開發‘, ‘哈哈哈‘, ‘2017-05-26 16:40:27‘);
//屢思路: //1. 功能:靜態頁 //導航條組件 + 面板組件(panel) + 表格組件 + 模態框組件 + 表單組件 //2. 列表功能 (顯示當前頁 + 顯示分頁) //2.1 發送ajax請求,獲取第一個的數據 ../api/findUsers.php currentPage=1 //2.2 發送前需要展示一個loading狀態, 獲取數據成功後把loading隱藏 //2.3 使用模版引擎渲染數據 //2.4 顯示分頁插件 ,要求這標簽必須是ul,而且生成出來的還是個行內塊元素,如果想要在最後下腳顯示, 包裹了一個div,text-right //分頁插件,有一個參數onPageClicked,可以獲取到page, 修改currentPage,重新渲染 //3. 刪除功能 //3.1 需要給頁面中的刪除按鈕註冊點擊事件(委托事件) //顯示刪除的模態框 //把刪除按鈕的id綁定模態框上(因為點擊模態框的確認按鈕時需要用到id) //3.2 給刪除模態框的確認按鈕註冊點擊事件 //1. beforeSend 給按鈕添加btn-loading, 不能會重復發送ajax請求 //2. 發送一個ajax請求刪除該用戶,需要傳遞id參數,模態框的自定義屬性 //3. success時, 清除按鈕的loading狀態, 隱藏模態框 渲染當前頁(如果只有一條數據,需要渲染前一頁) //4 添加與修改功能 //4.1 如果點擊的添加教師按鈕 //顯示模態框 // 給模態框添加了一個自定義屬性,type add // 修改標題 添加教師信息 // 重置表單 //4.2 如果點擊的是編輯按鈕 //顯示模態框 //給模態框添加了一個自定義屬性,type edit //修改標題 修改教師信息 // 通過自定義屬性id,找到id對應的用戶信息,, list保存當前頁所有的用戶,只需要遍歷比較id就能找出來。 // 把用戶信息設置到表單對應的值。 //4.3 給保存按鈕註冊點擊事件 //判斷是添加還是修改 //如果是添加, 發送請求到saveUser 渲染第一頁 //如果是修改, 發送請求到updateUser,渲染到當前頁 //發送ajax請求 //beforeSend:添加loading狀態,不能重復提交 //success: //關閉模態框 //移除loadingz狀態 //渲染當前頁。
11.2. 頁面初始化
- 導航組件
- panel組件
- table組件
- 模態框組件
- 表單組件
11.3. bootstrap-paginator分頁插件的使用
Bootstrap Paginator是一款基於Bootstrap的js分頁插件,功能很豐富,
github地址
$("#pagintor").bootstrapPaginator({
bootstrapMajorVersion:3,//默認是2,如果是bootstrap3版本,這個參數必填
currentPage:1,//當前頁
totalPages:10,//總頁數
size:"small",//設置控件的大小,mini, small, normal,large
onPageClicked:function(event, originalEvent, type,page){
//為按鈕綁定點擊事件 page:當前點擊的按鈕值
}
});
12. 同源與跨域
12.1. 同源
12.1.1. 同源策略的基本概念
1995年,同源政策由 Netscape 公司引入瀏覽器。目前,所有瀏覽器都實行這個政策。 同源策略:最初,它的含義是指,A網頁設置的 Cookie,B網頁不能打開,除非這兩個網頁"同源"。所謂"同源"指的是"三個相同"。
協議相同
域名相同
端口相同
舉例來說,http://www.example.com/dir/page.html
這個網址,協議是http://
,域名是www.example.com
,端口是80
(默認端口可以省略)。它的同源情況如下。
//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:不同源(端口不同)
12.1.2. 同源策略的目的
同源政策的目的,是為了保證用戶信息的安全,防止惡意的網站竊取數據。
設想:某用戶是網站A的管理員,用戶只要登錄網站A並且執行刪除用戶的操作就可以把網站A中的其他用戶刪除。當這個用戶訪問了網站A之後,又訪問了一個網站B,這時候如果網站B可以獲取到網站A的cookie,那麽網站B就能偽造該用戶的身份去訪問網站A,即網站B也可以執行相同的操作去刪除網站A中的其他用戶,那這樣帶來的後果是不堪設想的。
CSRF攻擊(跨站請求偽造),
因此同源策略是必須的,如果cookie可以在不同源的網站中共享,那麽互聯網將毫無安全可言。
12.1.3. 同源策略的限制範圍
隨著互聯網的發展,“同源策略”越來越嚴格,目前,如果非同源,以下三種行為都將收到限制。
//1. Cookie、LocalStorage 和 IndexDB 無法讀取。
//2. DOM 無法獲得。
//3. AJAX 請求不能發送。
雖然這些限制是很有必要的,但是也給我們日常開發帶來不好的影響。比如實際開發過程中,往往都會把服務器端架設到一臺甚至是一個集群的服務器中,把客戶端頁面放到另外一個單獨的服務器。那麽這時候就會出現不同源的情況,如果我們知道兩個網站都是安全的話,我們是希望兩個不同源的網站之間可以相互請求數據的。這就需要使用到跨域 。
12.2. 跨域
12.2.1. jsonp
JSONP(JSON with Padding)、可用於解決主流瀏覽器的跨域數據訪問的問題。原理:服務端返回一個預先定義好的javascript函數的調用,並且將服務器的數據以該函數參數的形式傳遞過來,這個方法需要前後端配合。
script
標簽是不受同源策略的限制的,它可以載入任意地方的 JavaScript 文件,而並不要求同源。類似的還有img
和link
標簽
<!--不受同源策略的標簽-->
<img src="http://www.api.com/1.jpg" >
<link rel="stylesheet" href="http://www.api.com/1.css">
<script src="http://www.api.com/1.js"></script>
12.2.1.1. jsonp演化過程1
php文件
header("content-type:text/html;charset=utf-8");
echo "alert(1111)";
html文件
<script src="http://www.api.com/testjs.php"></script>
原理:其實src的路徑是什麽文件不重要,無論引入js文件還是php文件,最後返回給瀏覽器的都是字符串,因此我們script標簽是可以引入一個php文件的。
12.2.1.2. jsonp演化過程2
php文件
header("content-type:text/html;charset=utf-8");
echo "var a = 118;";
html文件
<script src="http://www.api.com/testjs.php"></script>
<script>
//a打印出來了118
console.log(a);
</script>
我們現在做到了一件事情,從不同源的php文件中獲取到了數據
缺點:獲取數據的script標簽必須寫在使用的script標簽的前面,必須保證先有數據才能對數據進行渲染。
12.2.1.3. jsonp演化過程3
php代碼
header("content-type:text/html;charset=utf-8");
$arr = array(
"name"=>"zs",
"age"=>18
);
$result = json_encode($arr);
//這是一段js函數的調用的代碼,$result就是我們想要的數據
echo "func($result)";
js代碼
<script>
function func(data) {
console.log(data);
}
</script>
<script src="http://www.api.com/testjs.php"></script>
缺點:後端必須知道前端聲明的方法的名字,後端才能調用。
12.2.1.4. jsonp演化過程4
php代碼
header("content-type:text/html;charset=utf-8");
$arr = array(
"name"=>"zs",
"age"=>18
);
$result = json_encode($arr);
//這是一段js函數的調用的代碼,$result就是我們想要的數據
echo $_GET[‘callback‘]."($result)";
javascript代碼
function fun(data) {
console.log(data);
}
var button = document.querySelector("button");
button.onclick = function () {
var script = document.createElement("script");
script.src = "http://www.api.com/testjs.php?callback=fun";
document.body.appendChild(script);
}
說白了,jsonp的原理就是 借助了script標簽不受同源策略的限制,在服務端返回一個函數的調用,將數據當前調用函數的實參。 在瀏覽器端,需要程序要聲明一個函數,通過形參就可以獲取到服務端返回的對應的值。
jsonp原理大家知道即可,不用太過於去糾結這個原理,因此jquery已經幫我們封裝好了,我們使用起來非常的方便。
12.2.2. jquery對於jsonp的封裝
//使用起來相當的簡單,跟普通的get請求沒有任何的區別,只需要把dataType固定成jsonp即可。
$.ajax({
type:"get",
url:"http://www.api.com/testjs.php",
dataType:"jsonp",
data:{
uname:"hucc",
upass:"123456"
},
success:function (info) {
console.log(info);
}
});
13. XMLHttpRequest2.0
XMLHttpRequest是一個javascript內置對象,使得Javascript可以進行異步的HTTP通信。2008年2月,就提出了XMLHttpRequest Level 2 草案。
老版本的XMLHttpRequest的缺點:
//1. 僅支持傳輸文本數據,無法傳說二進制文件,比如圖片視頻等。
//2. 傳輸數據時,沒有進度信息,只能提示完成與否。
//3. 受到了"同源策略"的限制
新版本的功能:
//1. 可以設置timeout超時時間
//2. 可以使用formData對象管理表單數據
//3. 允許請求不同域名下的數據(跨域)(cors)
//4. 支持上傳二進制文件
//5. 可以獲取數據傳輸的進度信息
註意:我們現在使用new XMLHttpRequest創建的對象就是2.0對象了,我們之前學的是1.0的語法,現在學習一些2.0的新特性即可。
13.1. timeout設置超時
xhr.timeout = 3000;//設置超時時間
//當超時時,會觸發這個事件
xhr.ontimeout = function(){
alert("請求超時");
}
13.2. formData管理表單數據
formData對象類似於jquery的serialize方法,用於管理表單數據
//使用特點:
//1. 實例化一個formData對象, new formData(form); form就是表單元素
//4. formData對象可以直接作為 xhr.send(formData)的參數。註意此時數據是以二進制的形式進行傳輸。
//5. formData有一個append方法,可以添加參數。formData.append("id", "1111");
//6. 這種方式只能以post形式傳遞,不需要設置請求頭,瀏覽器會自動為我們設置一個合適的請求頭。
代碼示例:
//1. 使用formData必須發送post請求
xhr.open("post", "02-formData.php");
//2. 獲取表單元素
var form = document.querySelector("#myForm");
//3. 創建form對象,可以直接作為send的參數。
var formData = new FormData(form);
//4. formData可以使用append方法添加參數
formData.append("id", "1111");
//5. 發送,不需要指定請求頭,瀏覽器會自動選擇合適的請求頭
xhr.send(formData);
13.3. 文件上傳
以前,文件上傳需要借助表單進行上傳,但是表單上傳是同步的,也就是說文件上傳時,頁面需要提交和刷新,用戶體驗不友好,xhr2.0中的formData對象支持文件的異步上傳。
var formData = new FormData();
//獲取上傳的文件,傳遞到後端
var file = document.getElementById("file").files[0];
formData.append("file", file);
xhr.send(formData);
13.4. 顯示文件進度信息
xhr2.0還支持獲取上傳文件的進度信息,因此我們可以根據進度信息可以實時的顯示文件的上傳進度。
1. 需要註冊 xhr.upload.onprogress = function(e){} 事件,用於監聽文件上傳的進度.註意:需要在send之前註冊。
2. 上傳的進度信息會存儲事件對象e中
3. e.loaded表示已上傳的大小 e.total表示整個文件的大小
代碼參考:
xhr.upload.onprogress = function (e) {
inner.style.width = (e.loaded/e.total*100).toFixed(2)+"%";
span.innerHTML = (e.loaded/e.total*100).toFixed(2)+"%";
}
xhr.send(formData);
如果上傳文件超過8M,php會報錯,需要進行設置,允許php上傳大文件。
13.5. 跨域資源共享(CORS)
13.5.1. cors的使用
新版本的XMLHttpRequest對象,可以向不同域名的服務器發出HTTP請求。這叫做"跨域資源共享"(Cross-origin resource sharing,簡稱CORS)。
跨域資源共享(CORS)的前提
- 瀏覽器支持這個功能
- 服務器必須允許這種跨域。
服務器允許跨域的代碼:
//允許所有的域名訪問這個接口
header("Access-Control-Allow-Origin:*");
//允許www.study.com這個域名訪問這個接口
header("Access-Control-Allow-Origin:http://www.study.com");
13.5.2. CORS的具體流程(了解)
- 瀏覽器會根據同源策略 查看是否是跨域請求,如果同源,直接發送ajax請求。
- 如果非同源,說明是跨域請求,瀏覽器會自動發送一條請求(預檢請求 ),並不會攜帶數據,服務器接受到請求之後,會返回請求頭信息,瀏覽器查看返回的響應頭信息中是否設置了
header(‘Access-Control-Allow-Origin:請求源域名或者*‘);
- 如果沒有設置,說明服務器不允許使用cors跨域,那麽瀏覽器不會發送真正的ajax請求。
- 如果返回的響應頭中設置了
header(‘Access-Control-Allow-Origin:請求源域名或者*‘);
,瀏覽器會跟請求頭中的Origin: http://www.study.com
進行對比,如果滿足要求,則發送真正的ajax請求,否則不發送。 - 結論:跨域行為是瀏覽器行為,是瀏覽器阻止了ajax行為。服務器與服務器之間是不存在跨域的問題的
13.5.3. jsonp與cors的對比
- jsonp兼容性好,老版本瀏覽器也支持,但是jsonp僅支持get請求,發送的數據量有限。使用麻煩
- cors需要瀏覽器支持cors功能才行。但是使用簡單,只要服務端設置允許跨域,對於客戶端來說,跟普通的get、post請求並沒有什麽區別。
- 跨域的安全性問題:很多同學會覺得跨域能帶來安全性問題,其實並不會,因為跨域是需要服務端配合的 ,也就是說不論jsonp還是cors,如果沒有服務端的允許,瀏覽器是沒法做到跨域的。
//找對象 //1. 先註冊點擊事件 //2. 獲取文本域的value值 //3. 動態生成一個div,class是self,添加到messages //4. 發送一個ajax請求,獲取到服務端返回的數據 //5. 動態生成一個div,class是other,添加到messages
13.5.4. 其他的跨域手段
以下方式基本不用啊,了解即可:
1、頂級域名相同的可以通過domain.name來解決,即同時設置 domain.name = 頂級域名(如example.com) 2、document.domain + iframe 3、window.name + iframe 4、location.hash + iframe 5、window.postMessage()
其他跨域方式
ajax(同源與跨域)