多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# [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`属性将它们认为是同源的。 ![](https://segmentfault.com/img/bVxCld?w=717&h=404) ## 跨域请求的几种常用方法 ### 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=),我们会发现一些数据返回了,如下图: ![](https://segmentfault.com/img/bVxClb?w=1358&h=183) 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/)