標籤:
問題:
頁面中有一個按鈕,點擊之後會更新網頁中的一個盒子的內容。
Ajax可以很容易的滿足這種無須重新整理整個頁面就可以實現資料變換的需求。
但是,Ajax有一個缺點,就是他不允許跨域請求資源。
如果My Code在codepen上,我不能將我的資料放到codepen網站上,那麼我只能放到我自己的伺服器中,這樣的話,就無法通過Ajax訪問到這個資料了。
解決:
想要實現這種跨域資源請求,有很多解決辦法,列舉出一部分:
- 讓伺服器來載入遠端資料,然後在使用者請求時提供給瀏覽器。
- 用<script>或是<iframe>標籤載入外來檔案。(因為他們的src屬性允許獲得任何地方的資源。)
- W3C制定的Cross-Origin Resource Sharing(CORS,跨域資源共用)。
- JSONP
JSONP的誕生及原理:
jsonp的原理其實和第二種解決方案一模一樣,只不過他更加方便,然後這種跨域溝通的手段就被賦予了一個名字“JSONP”。
所以首先要弄懂第二種方式是怎麼工作的:
原理:如果一個頁面載入了一個外來的JS檔案,瀏覽器就會自動執行這個檔案中的代碼。
所以假如localhost想要使用jsonhost上面的一個JSON資料,localhost就可以讓jsonhost來幫他完成這件事情,jsonhost提供給他一個js檔案,往要調用的函數中傳入需要的資料,結果是和localhost自己調用函數的效果一模一樣了。
jsonhost:
<script type="text/javascript">
var json=‘["customername1","customername2"]‘;
callbackFunction(json);
</script>
localhost:
<script type="text/javascript">
var json=‘["customername1","customername2"]‘;
callbackFunction(json);
</script>
<script type="text/javascript"> function callbackFunction(result) { var html = ‘<ul>‘; for(var i = 0; i < result.length; i++) { html += ‘<li>‘ + result[i] + ‘</li>‘; } html += ‘</ul>‘; document.getElementById(‘divCustomers‘).innerHTML = html; }</script>
這樣,localhost就已經可以使用jsonhost中的資料了。
然後localhost說,我希望可以在我的使用者點擊一次按扭時,就執行一遍callbackFunction(json),而不是頁面載入後執行一次。
於是他就需要動態建立<script>標籤:
function callbackFunction(result){ var html = ‘<ul>‘; for(var i = 0; i < result.length; i++) { html += ‘<li>‘ + result[i] + ‘</li>‘; } html += ‘</ul>‘; document.getElementById(‘divCustomers‘).innerHTML = html;}$(".btn").click(function(){ var script = document.createElement(‘script‘); script.setAttribute(‘src‘, "http://jsonhost/json.js"); document.getElementsByTagName(‘header‘)[0].appendChild(script);});
這樣完成之後,效果就和用Ajax非同步請求一樣了。
到這裡,故事彷彿就要這樣結束了,但是突然有一天,另一個otherhost跑來和jsonhost說,他想要通過jsoncallbackFunction處理json,jsonhost就很為難,於是他們聚在一起,想要找到一個辦法,可以不需要全部使用同一個函數名,也可以擷取同一個資料。
最終他們想到了一個完美的辦法——jsonhost用的函數名用一個變數代替,localhost和otherhost請求資料的時候,傳入這個變數名,這樣就可以各自決定各自使用的函數名了。
jsonhost:
<?php header(‘Content-type: application/json‘); //告訴接收資料的對象此頁面輸出的是json資料$json = ‘["customername1","customername2"]‘;echo $_GET[‘callback‘] . "(" . $json . ")";?>
localhost:
<script type="text/javascript"> function getJson(url,funName){ var script = document.createElement(‘script‘); script.setAttribute(‘src‘, url+funName); document.getElementsByTagName(‘head‘)[0].appendChild(script); } function callbackFunction(result) { var html = ‘<ul>‘; for(var i = 0; i < result.length; i++) { html += ‘<li>‘ + result[i] + ‘</li>‘; } html += ‘</ul>‘; document.getElementById(‘divCustomers‘).innerHTML = html; } $(".btn").click(function(){ getJson("http://jsonhost/jsonp.php?jsoncallback=","callbackFunction"); });</script>
otherhost:
<script type="text/javascript"> function getJson(url,funName){ var script = document.createElement(‘script‘); script.setAttribute(‘src‘, url+funName); document.getElementsByTagName(‘head‘)[0].appendChild(script); } function jsoncallbackFunction(result) { console.log(result); } $(".btn").click(function(){ getJson("http://jsonhost/jsonp.php?jsoncallback=","jsoncallbackFunction"); });</script>
這樣一來,使用什麼函數名都是不同host自己的事情,他們互不干擾,jsonhost也不用操心這件事,專心提供資料就可以了。其他host也紛紛前來擷取json。於是這種模式被廣泛使用,然後這種通訊方式就被命名為“JSONP”。
如果用jQuery的話,就不用自己命名函數並傳遞給參數了,因為這個函數名一點也不重要,他只是個代號而已,jQuery會幫我們自動產生一個函數名,然後將得到的資料傳給這個函數。jQuery還會幫我們建立script標籤, 我們只要關心如何處理這個資料就好了。
<script src="http://apps.bdimg.com/libs/jquery/1.8.3/jquery.js"></script><script> $.getJSON("http://jsonhost/jsonp.php?jsoncallback=?", function(data) { var html = ‘<ul>‘; for(var i = 0; i < data.length; i++) { html += ‘<li>‘ + data[i] + ‘</li>‘; } html += ‘</ul>‘; $(‘#divCustomers‘).html(html); });</script>jQuery把JSONP封裝到Ajax裡面,但本質上這兩種技術是完全不同的。JSONP的原理是,當前網頁動態執行異域返回的js代碼,這個代碼是個執行請求資料的函數。瀏覽器執行這個函數,效果和當前域獲得資料執行函數是一樣的。
應用執行個體:
知道了原理之後,迫不及待的想要用一下JSONP來擷取資料。這裡用PHP來實現。
首先,需要有個伺服器,如果沒有伺服器的話,可以使用wampserver軟體類比一個,這個軟體還會建立一個Integration Environment,可以運行PHP檔案。點擊查看wampserver教程
有了自己的伺服器和PHP運行環境之後,就可以開始了。想要在codepen上擷取本機資料。
本機PHP:
<?php header(‘Content-type: application/json‘); //告訴接收資料的對象此頁面輸出的是json資料$quotes = ‘[{ "quote": "If you can\‘t get rid of the skeleton in your closet, you\‘d best teach it to dance.", "author": "George Bernard Shaw" }, { "quote": "We\‘ll always have Paris.", "author": "Casablanca" }, { "quote": "A mathematician is a device for turning coffee into theorems.", "author": "Paul Erdos" }, { "quote": "Do, or do not. There is no \‘try\‘.", "author": "Star Wars: Empire Strikes Back" }, { "quote": "Some cause happiness wherever they go; others, whenever they go.", "author": "Oscar Wilde" }, { "quote": "Problems worthy of attack prove their worth by fighting back.", "author": "Paul Erdos" }, { "quote": "Maybe this world is another planet\‘s Hell.", "author": "Aldous Huxley" }]‘;echo $_GET[‘callback‘] . "(" . $quotes . ")";?>
codepen上的js:
function update(){ var index=Math.floor(Math.random()*11); $.getJSON("http://localhost/quotes.php?callback=?",function(data){ var num=Math.floor(Math.random()*6); $(".wrap").fadeOut(600,function(){ $(".quo").html(data[num].quote); $(".auth").html(data[num].author); $("body, .quote-box button").css("background-color",colors[index]); $(".wrap").css("color",colors[index]); }).fadeIn(600); });}$(document).ready(function(){ update(); $(".update").click(update);});
點擊查看線上demo,必須將本機類比成伺服器並建立PHP環境並添加了PHP檔案才能運行。
而且要注意把codepen的https改成http。
成功之後可以看到,發送的請求中,傳給callback的是一個jQuery自動產生的函數:
返回的也是這個函數調用資料:
如果不想自己配置的話,可以應用其他網站提供的API,實現原理是一樣的。demo
參考:
- JSONP 教程
- 《jQuery基礎教程》
- 說說JSON和JSONP,也許你會豁然開朗,含jQuery用例
JSONP的誕生、原理及應用執行個體