# [mui初級入門教程(三)— html5+ XMLHttpRequest 與mui ajax用法詳解](https://www.cnblogs.com/PheonixHkbxoic/p/6013352.html)
## 寫在前面
這是這個系列的的第三篇文章,前面的文章在多個地方(本人`github`博客、`dcloud ask`社區、`segmentfault`)發出來了,很多朋友收藏點贊,只是沒有多少人反映內容的深淺,也沒有人提出意見,所以實話說不知道符不符合大家胃口,不過我寫博客一向以詳細為標準,盡可能照顧到各種人群,特別是入門級的同學,力求還原我學習這個東西的一個思路和過程,在文章中也分享一些不錯的干貨,最近在折騰博客,用`webpack`和`vue-cli`打包了一下,目前還有些問題有待解決,自己嘗試寫一個markdown編輯器因為bug過多的問題,然后開始使用馬克飛象寫博客,畢竟生成的界面美觀多了,這樣可以方便大家閱讀吧。廢話不多說,開始我們今天的內容,今天主要是學習一下`html5+`的`XMLHttpRequest`以及`mui`的基本用法。
## 基礎鋪墊
### Javascript XMLHttpRequest網絡請求
> XMLHttpRequest 是一個 JavaScript 對象,它最初由微軟設計,隨后被 Mozilla、Apple 和 Google采納. 如今,該對象已經被 W3C組織標準化. 通過它,你可以很容易的取回一個URL上的資源數據. 盡管名字里有XML, 但 XMLHttpRequest 可以取回所有類型的數據資源,并不局限于XML。 而且除了HTTP ,它還支持file 和 ftp 協議. ——[MDN XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest)
`XMLHttpRequest`讓發送一個`HTTP`請求變得非常容易。你只需要簡單的創建一個請求對象實例,打開一個`URL`,然后發送這個請求。當傳輸完畢后,結果的`HTTP`狀態以及返回的響應內容也可以從請求對象中獲取。
#### XMLHttpRequest網絡請求的一般步驟:
第一步:創建一個 XMLHttpRequest 實例
~~~
new XMLHttpRequest();
~~~
第二步:初始化HTTP請求參數
~~~
void open(
DOMString method,
DOMString url,
optional boolean async,
optional DOMString user,
optional DOMString password
);
~~~
* **method**:請求所使用的`HTTP`方法; 例如`"GET"`,`"POST"`,`"PUT"`,`"DELETE"`等. 如果下個參數是非`HTTP(S)`的`URL`,則忽略該參數.
* **url**:該請求所要訪問的`URL`
* **async**:一個可選的布爾值參數,默認為`true`,意味著是否執行異步操作,如果值為`false`,則`send()`方法不會返回任何東西,直到接受到了服務器的返回數據。如果為值為`true`,一個對開發者透明的通知會發送到相關的事件監聽者。這個值必須是`true`,如果`multipart`屬性是`true`,否則將會出現一個意外。
* **user**:用戶名,可選參數,為授權使用;默認參數為空`string`.
* **password**:密碼,可選參數,為授權使用;默認參數為空`string`.
第三步:發送請求
~~~
send();
~~~
發送請求. 如果該請求是異步模式(默認),該方法會立刻返回. 相反,如果請求是同步模式,則直到請求的響應完全接受以后,該方法才會返回.
如下例:
~~~
var xhr = new XMLHttpRequest();
xhr.onload = function () {
console.log(this.responseText);
};
xhr.onreadystatechange = function() {
console.log(this.readyState);
};
xhr.open("get", "https://www.baidu.com", true);
xhr.send();
~~~
我們在hbuilder里面打開,控制臺會報錯:
~~~
[Web瀏覽器] "XMLHttpRequest cannot?load https://www.baidu.com/.?No?'Access-Control-Allow-Origin' header?is present?on the requested resource. Origin?'http://127.0.0.1:8020'?is therefore?not allowed access."
~~~
用瀏覽器打開,按`F12`在控制臺`console`下查看也會發現上述錯誤,這是為啥呢?
這是因為普通網頁能夠使用`XMLHttpRequest`對象發送或者接受服務器數據, 但是它們受限于[同源策略](https://en.wikipedia.org/wiki/Same-origin_policy)。只要先獲取了跨域請求許可,就可以進行跨域請求。
> **同源策略**:如果兩個頁面的**協議**、**域名**和**端口**是完全相同的,那么它們就是同源的。同源策略是為了防止從一個地址加載的文檔或腳本訪問或者設置從另外一個地址加載的文檔的屬性。如果兩個頁面的主域名相同,則還可以通過設置`document.domain`屬性將它們認為是同源的。

## 跨域請求的幾種常用方法
### CORS,使用XMLHttpRequest對象
CORS 的全稱是 Cross-Origin Resource Sharing,即跨域資源共享。他的原理就是使用自定義的 HTTP 頭部,讓服務器與瀏覽器進行溝通,主要是通過設置響應頭的 Access-Control-Allow-Origin 來達到目的的。這樣,XMLHttpRequest 就能跨域了。想要進一步了解CORS的朋友可以看看這篇文章:[了解跨域資源共享 (CORS)](http://www.adobe.com/cn/devnet/html5/articles/understanding-cross-origin-resource-sharing-cors.html)
在服務器端添加響應頭`Access-Control-Allow-Origin`,使用`XMLHttpRequest`對象請求。值得注意的是,正常情況下的 XMLHttpRequest 是只發送一次請求的,但是跨域問題下很可能是會發送兩次的請求(預發送)。
PHP:
~~~
header('Access-Control-Allow-Origin: *');
~~~
java:
~~~
response.addHeader(?"Access-Control-Allow-Origin",?"*" );
~~~
大家不妨將上面那個例子中的地址換成這個試試:[http://zhaomenghuan.github.io...](http://zhaomenghuan.github.io/api/blog.json)
(僅用于學習測試,禁止直接用于任何不經征求本人同意的站點)
~~~
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open("get", "http://zhaomenghuan.github.io/api/blog.json", true);
xhr.onload = function () {
console.log(this.responseText);
var data = JSON.parse(this.responseText);
for(var i in data){
document.write(data[i].pageId + ':' + data[i].title+"<br />")
}
};
xhr.onerror = function () {
alert('error making the request.');
};
xhr.send();
</script>
</body>
</html>
~~~
哈哈,執行完這段代碼你會發現,你能夠查看到我博客的列表,所以大家是不是懂了些什么, CORS能否跨域實現需要服務器端支持,我們這里能夠收到信息,是因為github上的資源是支持CORS訪問的,這和框架無關,很多人以為用了框架就可以跨域,這都是不正確的理解,框架只是封裝了一些方法,讓我們使用更加方便,能不能跨域還是取決于我們服務器端的訪問權限。這里順便說一下很多人用mui會發現在瀏覽器端會報錯,在app環境能夠訪問,這是因為mui依賴html5+ XMLHttpRequest網絡請求模塊,后面會詳細講到。
### Jsonp,**只適用于get請求**
`Jsonp`的跨域不是用`XMLHttpRequest`實現的,而是一個`script`標簽,`script`是可以跨域的,回調函數作為`get`參數傳入請求里。
原理很簡單,比如你在A域名請求B域名:
* 在A域名的頁面中使用script標簽src寫成B域名中服務器的URLscript標簽是可以跨域的,比如你調用Google Map或Google Analytics時引入的js就是google域名下的。
* 后端程序在最后需要把一段js代碼的字符串打印出來,這樣就可以運行A域名js中寫好的callback方法,將要返回的數據放入參數就可以了
A域名中的js文件:
~~~
function CreateScript(src){
var el = document.createElement('script');
el.src = src;
el.async = true;
el.defer = true;
document.body.appendChild(el);
}
// 響應的方法
function jsonpcallback(rs) {
console.log(JSON.stringify(rs));
document.getElementById("output").innerHTML = JSON.stringify(rs);
}
// 發起get請求
CreateScript('http://127.0.0.1:8888?userid=001&callback=jsonpcallback');
~~~
B域名中node服務器:
~~~
var http = require('http');
var url = require('url');
var data = {
'name': 'zhaomenghuan',
'age': '22'
};
http.createServer(function(req, res){
// 將url字符串轉換成Url對象
var params = url.parse(req.url, true);
console.log(params);
// 查詢參數
if(params.query){
// 根據附件條件查詢
if(params.query.userid === '001'){
// 判斷是否為jsonp方式請求,若是則使用jsonp方式,否則為普通web方式
if (params.query.callback) {
var resurlt = params.query.callback + '(' + JSON.stringify(data) + ')';
res.end(resurlt);
} else {
res.end(JSON.stringify(data));
}
}
}
}).listen(8888);
~~~
這樣我們就可以在A域名下跨域請求,當然我們經常看到很多框架如jquery對jsonp進行了進一步封裝,但是基本原理同上面。這里給出兩個例子:
* [native-JavaScript實現](http://zhaomenghuan.github.io/demo/translation/native-JavaScript.html)
* [mui-jsonp實現](http://zhaomenghuan.github.io/demo/translation/mui-jsonp.html)
總之,不論是`XMLHttpRequest`的跨域,還是`Jsonp`,都是需要請求的網站服務器端提供支持,在愿意分享給你數據的情況下你才能得到。在對方沒有提供支持的情況下,你是取不到它的數據的。當然跨域的解決方案有很多種,由于本人沒有實踐過,沒有實踐就沒有發言權,這里給大家貼一個帖子,自己跨域自行驗證,[淺談瀏覽器端JavaScript跨域解決方法](https://segmentfault.com/a/1190000004518374#articleHeader3)。
### html5+ XMLHttpRequest網絡請求
上面我們花了很大篇幅講解了`JavaScript XMLHttpRequest`對象,而且也簡單的講解了同源策略和跨域請求的常用方法,我們注意到無論是`JavaScript XMLHttpRequest`還是`jsonp`都需要通過服務器端的支持才能實現跨域,另外還有幾種也有一定的局限性,所以總還是讓人覺得美中不足。`html5+`提供了一個`XMLHttpRequest`模塊,在`APP`端很完美的解決了這種問題,而且提供了和`JavaScript XMLHttpRequest`對象用法類似的一系列屬性方法。由于本文的重在在于講解`html5+ XMLHttpRequest`,所以前面的`JavaScript XMLHttpRequest`相關的屬性方法只是初略介紹了一下,下面重點介紹`html5+ XMLHttpRequest`模塊。
#### 適用范圍:
由于`html5+ XMLHttpRequest`是一種拓展方案,所以需要底層支持,基于`html5+ XMLHttpRequest`的方法不能用于非`5+`環境,如果對這些概念不清楚的歡迎先閱讀我之前寫的文章**[mui初級入門教程(一)— 菜鳥入手mui的學習路線](http://zhaomenghuan.github.io/#)**。
我們用`hbuilder`新建一個app工程,然后運行下面的代碼:
~~~
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title></title>
</head>
<body>
<script type="text/javascript">
document.addEventListener('plusready', function(){
var xhr = new plus.net.XMLHttpRequest();
xhr.onreadystatechange = function () {
switch ( xhr.readyState ) {
case 0:
console.log( "xhr請求已初始化" );
break;
case 1:
console.log( "xhr請求已打開" );
break;
case 2:
console.log( "xhr請求已發送" );
break;
case 3:
console.log( "xhr請求已響應");
break;
case 4:
if ( xhr.status == 200 ) {
alert( "xhr請求成功:"+xhr.responseText );
} else {
console.log( "xhr請求失敗:"+xhr.readyState );
}
break;
default :
break;
}
}
xhr.open("GET", "https://www.baidu.com/");
xhr.send();
}, false );
</script>
</body>
</html>
~~~
我們會發現最后彈出了彈框,里面寫有百度首頁的代碼。
#### 5+ XMLHttpRequest對象
創建一個`XMLHttpRequest`對象,對象創建時不觸發任何時間和網絡請求,需和`open`,`send`方法配合使用。
~~~
var xhr = new plus.net.XMLHttpRequest();
xhr.open(method, url);
xhr.send();
~~~
> **XMLHttpRequest的屬性:**
* **[readyState](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.readyState):**HTTP 請求的狀態
* **[response](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.response):**請求從服務器接收到的響應數據
* **[responseText](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.responseText):**請求從服務器接收到的響應數據(字符串數據)
* **[responseType](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.responseType):**請求響應數據response的類型
* **[responseXML](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.responseXML):**請求響應的Document對象
* **[status](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.status):**服務器返回的HTTP狀態代碼
* **[statusText](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.statusText):**服務器返回的HTTP狀態描述
* **[timeout](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.timeout):**請求服務器的超時時間,單位為毫秒(ms)
* **[withCredentials](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.withCredentials):**是否支持跨域請求
> **XMLHttpRequest的方法:**
* **[abort](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.abort):**取消當前響應,關閉連接并且結束任何未決的網絡活動
* **[getAllResponseHeaders](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.getAllResponseHeaders):**獲取HTTP響應頭部信息
* **[getResponseHeader](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.getResponseHeader):**獲取指定的HTTP響應頭部的值
* **[open](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.open):**初始化HTTP請求參數,例如URL和HTTP方法,但是并不發送請求
* **[send](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.send):**發送HTTP請求
* **[setRequestHeader](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.setRequestHeader):**指定一個HTTP請求的Header
> **XMLHttpRequest的事件:**
* **[onreadystatechange](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.onreadystatechange):**網絡請求狀態發生變化事件
* **[onloadstart](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.onloadstart):**網絡請求開始事件
* **[onprogress](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.onprogress):**網絡請求傳輸數據事件
* **[onabort](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.onabort):**網絡請求取消事件
* **[onerror](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.onerror):**網絡請求錯誤事件
* **[onload](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.onload):**網絡請求成功事件
* **[ontimeout](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.ontimeout):**網絡請求超時事件
* **[onloadend](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.onloadend):**網絡請求結束事件
### mui ajax
`mui`框架基于`htm5plus`的`XMLHttpRequest`,封裝了常用的`Ajax`函數,支持`GET`、`POST`請求方式,支持返回`json`、`xml`、`html`、`text`、`script`數據類型; 本著極簡的設計原則,`mui`提供了`mui.ajax`方法,并在`mui.ajax`方法基礎上,進一步簡化出最常用的`mui.get()`、`mui.getJSON()`、`mui.post()`三個方法。
> **mui.ajax( url \[,settings\] )**
* **url**:請求發送的目標地址
* **settings**:key/value格式的json對象,用來配置ajax請求參數,支持的參數如下:
* * **data**:發送到服務器的業務數據;
* **type**:請求方式,目前僅支持'GET'和'POST',默認為'GET'方式;
* **dataType**:預期服務器返回的數據類型;如果不指定,mui將自動根據HTTP包的MIME頭信息自動判斷;
支持設置的dataType可選值:
* "xml": 返回XML文檔
* "html": 返回純文本HTML信息;
* "script": 返回純文本JavaScript代碼
* "json": 返回JSON數據
* "text": 返回純文本字符串
* **success**:Type: Functon(Anything data,String textStatus,XMLHttpRequest xhr)
請求成功時觸發的回調函數,該函數接收三個參數:
* data:服務器返回的響應數據,類型可以是json對象、xml對象、字符串等;
* textStatus:狀態描述,默認值為'success'
* xhr:xhr實例對象
* **error**:Type: Functon(XMLHttpRequest xhr,String type,String errorThrown)請求失敗時觸發的回調函數;
該函數接收三個參數:
~~~
- xhr:xhr實例對象
- type:錯誤描述,可取值:"timeout", "error", "abort", "parsererror"、"null"
- errorThrown:可捕獲的異常對象
~~~
* **timeout**:Type: Number,請求超時時間(毫秒),默認值為0,表示永不超時;若超過設置的超時時間(非0的情況),依然未收到服務器響應,則觸發error回調;
* **headers**:Type: Object,格式為:`{'Content-Type':'application/json'}`,
詳情參考[html5+ setRequestHeader](http://www.html5plus.org/doc/zh_cn/xhr.html#plus.net.XMLHttpRequest.setRequestHeader)。
**基本格式如下:**
~~~
mui.ajax(url,{
data:{
username:'username',
password:'password'
},
dataType:'json',//服務器返回json格式數據
type:'post',//HTTP請求類型
timeout:10000,//超時時間設置為10秒;
success:function(data){
//服務器返回響應,根據響應結果,分析是否登錄成功;
...
},
error:function(xhr,type,errorThrown){
//異常處理;
console.log(type);
}
});
~~~
> **mui.post( url \[,data\] \[,success\] \[,dataType\] )**
`mui.post()`方法是對`mui.ajax()`的一個簡化方法,直接使用`POST`請求方式向服務器發送數據、且不處理`timeout`和異常(若需處理異常及超時,請使用`mui.ajax()`方法)
~~~
mui.post('http://server-name/login.php',{
username:'username',
password:'password'
},function(data){
//服務器返回響應,根據響應結果,分析是否登錄成功;
...
},'json'
);
~~~
> **mui.get( url \[,data\] \[,success\] \[,dataType\] )**
`mui.get()`方法和`mui.post()`方法類似,只不過是直接使用`GET`請求方式向服務器發送數據、且不處理`timeout`和異常(若需處理異常及超時,請使用`mui.ajax()`方法)。如下為獲得某服務器新聞列表的代碼片段,服務器以json格式返回數據列表:
~~~
mui.get('http://server-name/list.php',
{category:'news'},
function(data){
//獲得服務器響應
...
},'json'
);
~~~
> **mui.get( url \[,data\] \[,success\] )**
`mui.getJSON()`方法是在`mui.get()`方法基礎上的更進一步簡化,限定返回`json`格式的數據,其它參數和`mui.get()`方法一致,如上獲得新聞列表的代碼換成`mui.getJSON()`方法后,更為簡潔,如下:
~~~
mui.getJSON('http://server-name/list.php',
{category:'news'},
function(data){
//獲得服務器響應
...
}
);
~~~
mui在getJSON基礎上封裝了一個jsonp插件,具體dem可以參考這里:[mui-jsonp實現有道詞典翻譯](http://zhaomenghuan.github.io/demo/translation/mui-jsonp.html)
> 注:初學者肯對于GET與POST的區別不是很清楚,這里不做詳細介紹,若要深入了解請查看:[GET,POST — 簡述](https://segmentfault.com/a/1190000004829968)
## 項目實戰
我們接著上一篇文章的項目開始進行本節的內容,上一篇我們講解了`html5+ webview`的使用方法,并且實現了一個基于父子`webview`的`tab bar`切換的案例,我們這次利用網易音樂`API`接口請求數據,完成我們后續的音樂播放器功能。
### API分析
網易音樂搜索API:
* **url**:[http://s.music.163.com/search...](http://s.music.163.com/search/get/)
* **type**:"GET"或“POST" //HTTP請求類型
* 請求參數:
* **type**: 1
* **s**: //關鍵詞
* **limit**: 10 //限制返回結果數為10
* **offset**: 0 //偏移
* **src**: lofter //可為空
* **filterDj**: true | false //可為空
* **callback**: //為空時返回json,反之返回jsonp callback
由于HTTP請求類型包含get,我們只需要把請求的參數拼接到url里面就可以得到返回的數據,格式為url?key1=value1&key2=value2。
在這個例子中我們打開這個網址[http://s.music.163.com/search...](http://s.music.163.com/search/get/?type=1&limit=5&s=),我們會發現一些數據返回了,如下圖:

OK,我們下面講解怎么通過程序得到這些數據,并且我們并且怎么解析這些數據。
在開始之前我們先對我們要得到的數據進行美化,這里我在網上隨便搜了一個
[在線JSON校驗格式化工具](http://www.bejson.com/),我們只需要把剛剛那些數據復制粘貼到我給的這個工具里面,然后點擊校驗就變整齊了。
~~~
{
"result": {
"songCount": 3224,
"songs": [
{
"id": 28949444,
"name": "喜歡你",
"artists": [
{
"id": 7763,
"name": "G.E.M.鄧紫棋",
"picUrl": null
}
],
"album": {
"id": 2956076,
"name": "喜歡你",
"artist": {
"id": 0,
"name": "",
"picUrl": null
},
"picUrl": "http://p1.music.126.net/u_1EudmF8Swgow6vfgYe1g==/8896148580676276.jpg"
},
"audio": "http://m2.music.126.net/_icR1apQHVl8wa0EP_REkQ==/3269947581061892.mp3",
"djProgramId": 0
}
]
},
"code": 200
}
~~~
我們可以看到
~~~
"picUrl":"http://p1.music.126.net/u_1EudmF8Swgow6vfgYe1g==/8896148580676276.jpg"
~~~
打開這個圖片地址我們就可以看到鄧紫棋了,哈哈,也就是說我們只需要抓取到這個地址就可以進行下面的工作咯,其他的類似。
### mui.ajax請求實例
我們在上次新建的M-BOX下的home.html下寫這個例子:
~~~
<script src="../js/mui.min.js"></script>
<script type="text/javascript">
var url = "http://s.music.163.com/search/get/";
mui.ajax(url,{
data: {
'type': 1,
's': "喜歡你",
'limit': 10
},
dataType:'json',//服務器返回json格式數據
type:'post',//HTTP請求類型
timeout:10000,//超時時間設置為10秒;
success:function(data){
console.log(JSON.stringify(data));
},
error:function(xhr,type,errorThrown){
//異常處理;
console.log(type);
}
});
</script>
~~~
`hbuilder`很方便的一個功能就是真機調試,我們直接將電腦與手機連接,通過`console.log()`函數可以很方便的將數據在控制臺打印出來,例如上述例子如果我們在`success`的回調函數中執行`console.log(data);`,在控制臺會輸出`[object Object]`,我們只需要利用`JSON.stringify()`方法可以將任意的`JavaScript`值序列化成`JSON`字符串。
### Javascript JSON語法基礎
每次看到很多同學在解析`json`的時候錯誤百出,嚴重暴漏了基本功,這里我就總結幾條與解析`json`可能相關的知識點。
#### 什么是JSON?
**JSON**:`JavaScript Object Notation`(JavaScript 對象表示法),`JSON`是存儲和交換文本信息的語法,獨立于語言。類似`XML`。`JSON`比`XML`更小、更快,更易解析,具有自我描述性,更易理解的特點。
`JSON`就是一串字符串,只不過元素會使用特定的符號標注。
* {} 雙括號表示對象
* \[\] 中括號表示數組
* "" 雙引號內是屬性或值
* :表示后者是前者的值(這個值可以是字符串、數字、也可以是另一個數組或對象)
`{"name": "Dcloud"}`可以理解為是一個包含`name`為`Dcloud`的對象;
`[{"name": "mui"},{"name": "html5+"}]`就表示包含兩個對象的數組。
通過`JavaScript`,您可以創建一個對象數組,并像這樣進行賦值:
~~~
var employees = [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName": "Jones" }
];
~~~
可以像這樣訪問 JavaScript 對象數組中的第一項:
`employees[0].lastName;`
返回的內容是:
`Doe`
可以像這樣修改數據:
`employees[0].firstName = "Jonatan";`
#### JSON.stringify和JSON.parse
這里需要特別說明的是兩個非常有用的方法:JSON.stringify和JSON.parse。我們可以通過JSON.stringify將json對象轉成json字符串,方便用alert,console.log打印出來,可以方便的查看json對象內容。反之我們可以通過JSON.parse將json字符串轉成json對象以便可以方便對json對象取值。例:
~~~
// 場景1
var data = {
name: 'zhaomenghuan',
age: 22
}
console.log(typeof data); // "object"
console.log(JSON.stringify(data)) // "{"name":"zhaomenghuan","age":22}"
// 場景2
var str = '{"name":"zhaomenghuan","age":22}';
console.log(typeof str) // "string"
console.log(JSON.parse(str).name) // "zhaomenghuan"
~~~
上面的例子列舉了兩種應用場景,大家根據自己的情況合適的選取正確的方法。
#### 運算符\[ \] 和 .
**\[ \]**可以用于數組和對象取值,數組中按下邊取值。
~~~
var array=["one","two","three","four"];
array[0];
~~~
對象屬性取值時,當我們不知道屬性名或屬性名本身包含點(**.**)的鍵,應當使用**\[ \]**。
~~~
for(var key in obj){
console.log(key + ":" + obj[key]);
}
~~~
~~~
var obj={
id:"obj",
"self.ref":ref
}
console.log(obj["self.ref"]);
~~~
**.**運算符的左邊為一個對象,右邊為屬性名。如:obj.id
#### 可以動態訪問屬性:
~~~
var key="property";
console.log(key);
var obj={
property:"hello word"
}
console.log(obj[key]);
~~~
#### 數組的map方法:
~~~
var array=[1,2,3,4,5];
array.map(function(item){
return item*2;
});
~~~
數組`map`方法可以接受一個匿名函數,數組中每個元素都會調用這個匿名函數,并且講返回結果放在一個數組中。
### 獲取數據更新UI
`mui`中的`each()`方法既是一個類方法,同時也是一個對象方法,兩個方法適用場景不同;換言之,你可以使用`mui.each()`去遍歷數組或`json`對象,也可以使用`mui(selector).each()`去遍歷`DOM`結構。
> **mui.each( obj , handler )**
* **obj**:
`Type: Array||JSONObj`
需遍歷的對象或數組;若為對象,僅遍歷對象根節點下的key
* **handler**:
`Type: Function( Integer||String index,Anything element)`
為每個元素執行的回調函數;其中,index表示當前元素的下標或key,element表示當前匹配元素
對于前面說到的音樂`api`返回的`json`數據,首先我們要通過`var songs=data.result.songs;`獲取`"songs"`的值,然后遍歷`"songs"`對象的子對象。
~~~
···
success:function(data){
// console.log(JSON.stringify(data));
var songs=data.result.songs;
mui.each(songs,function(index,item){
var id = item.id,
name = item.album.name,
author = item.artists[0].name,
picUrl = item.album.picUrl,
audio = item.audio;
})
},
···
~~~
我們用上述代碼可以得到我們想要的數據,下面就是需要將信息展示出來,這里我們只獲取 歌曲`id`、歌曲所屬專輯名`name`、歌曲第一作者`author`、歌曲所屬專輯圖片`picUrl`、歌曲音頻文件`audio`。
考慮到要加載很多圖片,這里我們使用懶加載實現效果,這里我們直接使用`hello mui`里面的模板頁面`lazyload-image.html`,我們需要引入`mui.lazyload.js`和`mui.lazyload.img.js`兩個文件,還有占位圖。下面先貼出修改后的基本的代碼,然后再講解其中的內容。
**home.html:**
~~~
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>M-BOX</title>
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!--標準mui.css-->
<link rel="stylesheet" href="../css/mui.min.css">
<style type="text/css">
.mui-content>.mui-table-view:first-child {
margin-top: 0px;
}
</style>
</head>
<body>
<div class="mui-content">
<ul id="list" class="mui-table-view mui-table-view-chevron"></ul>
</div>
</body>
<script src="../js/mui.min.js "></script>
<script src="../js/mui.lazyload.js"></script>
<script src="../js/mui.lazyload.img.js"></script>
<script>
mui.init();
var url = "http://s.music.163.com/search/get/";
mui.ajax(url,{
data: {
'type': 1,
's': "喜歡你",
'limit': 10
},
dataType:'json',//服務器返回json格式數據
type:'post',//HTTP請求類型
timeout:10000,//超時時間設置為10秒;
success:function(data){
//console.log(JSON.stringify(data));
var songs=data.result.songs;
var list = document.getElementById("list");
var fragment = document.createDocumentFragment();
var li;
mui.each(songs,function(index,item){
var id = item.id,
name = item.album.name,
author = item.artists[0].name,
picUrl = item.album.picUrl,
audio = item.audio;
li = document.createElement('li');
li.className = 'mui-table-view-cell mui-media';
li.innerHTML = '<a class="mui-navigate-right" id='+ id +' data-audio='+ audio +'>'+
'<img class="mui-media-object mui-pull-left" data-lazyload="'+picUrl+'">'+
'<div class="mui-media-body">'+name+
'<p class="mui-ellipsis">'+author+'</p>'+
'</div>'+
'</a>';
fragment.appendChild(li);
})
list.appendChild(fragment)
mui(document).imageLazyload({
placeholder: '../img/60x60.gif'
});
},
error:function(xhr,type,errorThrown){
//異常處理;
console.log(type);
}
});
//列表點擊事件
mui("#list").on('tap','li a',function(){
var id = this.getAttribute('id');
var audio = this.getAttribute('data-audio');
//打開詳情頁面
mui.openWindow({
url:'music.html',
id:'music.html',
extras:{
musicId:id,
audioUrl:audio
}
});
});
</script>
</html>
~~~
**music.html:**
~~~
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title></title>
<link href="../css/mui.min.css" rel="stylesheet"/>
</head>
<body>
<script src="../js/mui.min.js"></script>
<script type="text/javascript">
mui.init();
mui.plusReady(function(){
var self = plus.webview.currentWebview();
var musicId = self.musicId;
var audioUrl = self.audioUrl;
document.write("musicId:" + musicId + "<br />");
document.write("audioUrl:" + audioUrl);
});
</script>
</body>
</html>
~~~
我們這里說幾個開發者常見的問題:
#### 拼接html字符串
首先打開`hello mui`里面的模板頁面`lazyload-image.html`,我們打開控制臺查看`elements`,結構如下:
~~~
<ul id="list" class="mui-table-view mui-table-view-chevron">
<li class="mui-table-view-cell mui-media">
<a class="mui-navigate-right">
<img class="mui-media-object mui-pull-left" data-lazyload-id="0" src="http://www.dcloud.io/hellomui/images/1.jpg?version=447.4190210457891">
<div class="mui-media-body">
主標題
<p class="mui-ellipsis">列表二級標題</p>
</div>
</a>
</li>
</ul>
~~~
我們需要將獲取的內容動態的填充到這個里面,這里常用的方法就是動態的生成節點,這里我們就要用到`DOM`的創建方法`document.createElement`,通過這個方法我們可以生成一個節點:
`li = document.createElement('li');`這個可以生成一個`li`節點,我們需要為這個`li`指定`class`,通過對對象的`className`賦值實現,如:`li.className = 'mui-table-view-cell mui-media';`,我們使用`appendChild(li)`方法將`li`節點掛著父節點上,比如:
~~~
var list = document.getElementById("list");
list.appendChild(li);
~~~
這個就是把`li`節點掛在`list`下,同理我們可以在`li`節點下 創建子節點,上面為了簡單,我們使用給`innerHTML`屬性賦值的做法,這樣`innerHTML`的值同樣會掛在`li`節點下,大家需要注意的是`innerHTML`屬性的值我字符串,所以我們往中間插入了變量,要使用`+`連接起來。我們可以通過循環創建多個節點,如下面這樣創建十個段落:
~~~
for(var i = 0 ; i < 10; i ++) {
var p = document.createElement("p");
var oTxt = document.createTextNode("段落" + i);
p.appendChild(oTxt);
document.body.appendChild(p);
}
~~~
上面我們使用的是`mui.each()`方法,效果類似。
如果細心的同學或許注意到我們使用了一個奇怪的寫法:
~~~
var fragment = document.createDocumentFragment();
mui.each(songs,function(index,item){
...
fragment.appendChild(li);
});
list.appendChild(fragment);
~~~
為啥我們要在這里使用`document.createDocumentFragment()`呢?
如果是對前端技術感興趣的同學說不定回去查一下,我相信大部分人可能就放過了吧!這里我查了一下資料:
> 在《javascript高級程序設計》一書的6.3.5:創建和操作節點一節中,介紹了幾種動態創建html節點的方法,其中有以下幾種常見方法:
* crateAttribute(name): 用指定名稱name創建特性節點
* createComment(text): 創建帶文本text的注釋節點
* createDocumentFragment(): 創建文檔碎片節點
* createElement(tagname): 創建標簽名為tagname的節點
* createTextNode(text): 創建包含文本text的文本節點
其中最感興趣且以前沒有接觸過的一個方法是createDocumentFragment()方法,書中介紹說:在更新少量節點的時候可以直接向document.body節點中添加,但是當要向document中添加大量數據是,如果直接添加這些新節點,這個過程非常緩慢,因為每添加一個節點都會調用父節點的appendChild()方法,為了解決這個問題,可以創建一個文檔碎片,把所有的新節點附加其上,然后把文檔碎片一次性添加到document中。——[document的createDocumentFragment()方法](http://www.cnblogs.com/yunfour/archive/2011/06/21/2085911.html)
#### 頁面傳值
我們點擊了列表,我們會進入一個詳情頁,我們肯定是想知道我們究竟點了哪一個我們想把一些數據傳入到詳情頁面,這里我們使用了`mui.openWindow()`方法中的拓展參數`extras`傳入幾個值,在詳情頁面通過`plus.webview.currentWebview`獲取:
~~~
mui.plusReady(function(){
var self = plus.webview.currentWebview();
var musicId = self.musicId;
var audioUrl = self.audioUrl;
document.write("musicId:" + musicId + "<br />");
document.write("audioUrl:" + audioUrl);
});
~~~
這是在頁面初始化時,通過擴展參數`extras`傳值;另外還有頁面已創建,通過自定義事件傳值,參考mui官網中[自定義事件](http://dev.dcloud.net.cn/mui/event/#customevent)的介紹,這里先不講得太多,后面會專門花時間詳細講解相關細節。
#### jsonp實現
由于前面已經詳細講解,這里就不再贅述,只給出一個基本的demo:[jsonp獲取網易云音樂](http://zhaomenghuan.github.io/utils/music.html)。
當我們寫到這里,發現這篇依然是長長的一篇干貨,很多內容摘自文檔,主要是為了新手可以詳細去了解整個流程。其實還有很多沒有寫出來,限于篇幅還是后面再寫吧!由于代碼在文章中寫得很詳細,工程代碼先不給出來,最后整個系列寫完了再放出來吧!
## 隨便聊聊
這段時間在技術上花得時間確實太多,剛剛和女朋友在圖書館學習,她和我開個玩笑,我在忙著做作業因為不喜歡被打擾,然后就覺得很煩躁,然后就聊著聊著她說和我分手,我不知道她是不是認真的,只是覺得空空的,自從走上代碼這條路,我已經失去太多東西,我不想失去女朋友,畢竟談了三年,我是真的愛她,寫完這個就去找她道歉吧!還有要向環頭道歉,昨天可能說話有點直接,不小心傷到他了,后來想想真的可能是我太較真,他只是想玩玩程序,我何必那種執著呢?如果環頭看到這篇博客,希望你可以原諒哥,我只是不想讓你走彎路,或許每個都應該自己去把握自己的人生,我們不必去干涉。
本人博客歡迎轉載!但請注明出處!本人博客若有侵犯他人之處,望見諒,請聯系我。希望互相關注,互相學習 --[PheonixHkbxoic](http://www.cnblogs.com/PheonixHkbxoic/)