呃,本來我覺得這個話題沒什么好說的了,因為如今好像沒有幾個web應(yīng)用能離開這類request,google和facebook用iframe來做comet的時候也基本上把能hack的都hack遍了,所以我估計開發(fā)者社區(qū)里應(yīng)該早就形成所謂的”最佳實踐”(best practices)了罷。不過最近看到有一些關(guān)注前端技術(shù)的blog(比如realazy)在討論相關(guān)的話題,發(fā)現(xiàn)還是有一些東西值得寫下來。
先說跨域的問題,首先要指出的是,iframe里的js宿主對象一樣也躲不開同源策略(same origin policy),僅僅能解決二級域名的跨域而已,比如www.tudou.com和so.tudou.com,如果要請求某個八桿子打不到一起去的域名下的數(shù)據(jù)(例如你想搞mashup),建議老老實實的用script標(biāo)簽去請求jsonp罷。關(guān)于jsonp要附帶說一下的是,jquery對jsonp請求的封裝方式很值得提倡:
用jsoncallback作為服務(wù)器端支持的標(biāo)準(zhǔn)jsonp參數(shù),而每次執(zhí)行這個方法都會用時間戳生成一個唯一的全局函數(shù)名,替換這個“?”,這個細節(jié)被封裝到黑盒里,使用者不必了解,可以像普通的ajax請求一樣,用匿名的回調(diào)函數(shù)作為最末尾的參數(shù)(這是jquery強調(diào)的風(fēng)格),這種語法糖(syntactic sugar)的作用絕對不僅僅是讓前端開發(fā)人員可以偷懶而已,對代碼的可讀性,兼容性和今后的維護都有好處。(我經(jīng)常要向服務(wù)器端的開發(fā)人員解釋這個道理,否則他們才不給你支持什么jsoncallback參數(shù)呢,直接給你返回一個“yy({……})”就算完工……囧)
當(dāng)然了,我們還可以走一些目前來看比較野的路子來實現(xiàn)跨域,比如在頁面里嵌入一個不到1k的swf,借助flashplayer向部署了crossdomain.xml的服務(wù)器請求數(shù)據(jù),再用actionscript里的externalinterface類把數(shù)據(jù)還給javascript(我覺得這種方法忒有調(diào)用dll的感覺~大心)。
我們還可以指望ie8里支持的xdomainrequestallowed,和firefox3.1支持的access control,甚至傳說中的html5 socket……噢喔喔,多么甜美的夢……/掐一把臉蛋
跟script標(biāo)簽一樣,iframe也可以用來替代ajax,而且在修改document.domain之后(比如上面提到的兩個域名,可以設(shè)置document.domain = “tudou.com”),還可以解決部分跨域問題。
通過iframe請求數(shù)據(jù)的方法,最直接的莫過于在頁面里動態(tài)的嵌入一個iframe標(biāo)簽,用它的src屬性直接請求包含數(shù)據(jù)的網(wǎng)頁,然后利用那個網(wǎng)頁里的js把數(shù)據(jù)傳給父頁面,比如:
這種方法耦合的太緊,非常不推薦。你請求的uri代表一個資源,應(yīng)該是單純的數(shù)據(jù),它會作為xml,json,js代碼還是html來處理,這個并不重要,不應(yīng)該把你的程序邏輯跟數(shù)據(jù)混雜到一起,數(shù)據(jù)也不應(yīng)該因為跨域或不跨域,用iframe,script還是ajax來請求就變成完全不同的東西。
有人會說:為了讓數(shù)據(jù)能夠被js處理,返回的內(nèi)容難免有差異。——但是小的差異可以通過合理的封裝隱藏起來,比如jquery的getjson方法
有人會說:請求的uri是一個動態(tài)頁面,同樣可以在uri里支持類似jsoncallback的參數(shù),生成一個script標(biāo)簽和其中的js代碼,把數(shù)據(jù)“包裹”在js里,比如請求“http://yoursite.com/request_url/?callback=cb1304344”,返回:
——首先,很多情況下你請求到的不會是動態(tài)頁面,在這個到處都強調(diào)高負載的web世界里,你拿到的經(jīng)常是squid之類的代理程序返回的緩存。其次,如果你請求的是html格式的文本,為了能作為js代碼來執(zhí)行,服務(wù)器端必須對這段文本做轉(zhuǎn)義和清理工作,而且安全性還不一定能保證(因為html里經(jīng)常包含很多來自ugc的內(nèi)容),如果你請求的是json格式的數(shù)據(jù)……那何必用iframe咧……直接嵌script罷……
我推薦在上述方法的基礎(chǔ)上做改良,首先在服務(wù)器端,直接返回數(shù)據(jù)本身,并且把數(shù)據(jù)“包裹”在一個textarea標(biāo)簽里,比如:
textarea的優(yōu)點是可以支持任何格式的內(nèi)容,而且這些內(nèi)容不會在iframe子頁面里解析(比如創(chuàng)建dom樹,執(zhí)行js),接下來前端要做的,只是在父頁面里獲取到子頁面的dom,把textarea的內(nèi)容取出來(注意不能取innerhtml而要取value)。
這里存在一個判斷iframe是否加載完成的問題,解決方法之一是在iframe標(biāo)簽上寫onload事件,不過這樣就需要顯式的調(diào)用一個函數(shù)。
方法二如下:
最后我們可以封裝出這樣一個方法:
只要再增加一個可選的param參數(shù),這就是一個很標(biāo)準(zhǔn)的jquery ajax api,我們還可以在jquery的$.get上面封裝,增加一個是否跨域的判斷,當(dāng)這個request的uri修改成同樣的域名后,自動切換到普通的ajax方法來請求,把返回的文本用類似這樣的正則/<(textarea)>(.+)<//(textarea)>/刪掉多余的字符,再傳給回調(diào)函數(shù),前端和服務(wù)器端都不用修改代碼。
必須指出的是,iframe在ie里獲取數(shù)據(jù)時會引發(fā)一些“小問題”,dojo的創(chuàng)始人alex russell把它們稱作“靈異點擊(phantom click)”和“噩夢般的指示器(throbber of doom)”,前者是指在iframe請求內(nèi)容的時候會出現(xiàn)一次點擊鏈接的音效(讓用戶懷疑鬧鬼,多差的體驗口牙!),后者是指iframe加載過程中,ie的界面上會出現(xiàn)正在讀取的提示(比如左下的進度條,右上的圖標(biāo))……好罷,其實以我個人的標(biāo)準(zhǔn),這兩個問題都可以無視……
這種方法還有一個明顯的缺陷,就是只支持get類型的請求。
不過iframe還有一種使用方法,不但可以避免上面提到的問題,也不需要服務(wù)器端做任何調(diào)整,簡單來說:在iframe的src里調(diào)用一個包含ajax方法的頁面,然后父頁面調(diào)用這個方法來發(fā)起跟子頁面同域名下的ajax請求。在土豆網(wǎng)的播放頁面上,我使用這種方法請求用戶評論統(tǒng)一接口里的html內(nèi)容,例如這個wh40k:dowii的視頻:
http://www.tudou.com/programs/view/ipcprdz_lhi/
獲得評論部分html的接口類似這樣:
http://comments.tudou.com/itemcomment.srv?method=get&iid=21283123&page=1&tm=5&ban=1
這個接口在獨立的一組服務(wù)器上實現(xiàn),在視頻播放頁,豆單播放頁,豆單封面,相冊,個人主頁都會被調(diào)用。由于包含大量用戶提交的內(nèi)容和復(fù)雜的html結(jié)構(gòu),如果用json形式,前端后端處理起來都效率低,此外,提交新評論,回復(fù),刪除,也會用到comments.tudou.com這個域名下的接口,而這些操作顯然需要post類型的請求。在這種需求下,借助iframe的ajax方法
首先在comments.tudou.com域名下部署一個供iframe調(diào)用的跨域文件,感覺很像flashplayer的crossdomain.xml……
http://comments.tudou.com/crossdomain/index.html
可以看到源文件里僅僅包含一個stand-alone的ajax方法……呃……你覺得很眼熟?不用懷疑,就是在jquery源代碼的基礎(chǔ)上修改來的-___-b,支持最基本的需求。這個頁面可以設(shè)置很長的過期頭讓瀏覽器緩存起來,因為不會再有變動。
在父頁面里通過tui.videocomment.request提供統(tǒng)一的接口,不做詳述了,只列舉其中訪問跨域方法的部分:
iframe適用于 ( 跨域的 && ( 返回大量數(shù)據(jù) || 返回html內(nèi)容 || 需要發(fā)post請求 ) ) 的場合,除此之外還有comet里的串流技術(shù)(streaming)——本文不涉及。使用時需要注意保持資源的純粹性,并盡可能隱藏那些跟其他異步請求差異很大的或包含hack的細節(jié)(比如嵌入iframe,觸發(fā)回調(diào)函數(shù),處理數(shù)據(jù)),設(shè)計出一致的,兼容性和擴展性良好的,不礙眼的接口xd
關(guān)于跨域還要補充一點:修改document.domain可能會產(chǎn)生一些無法預(yù)料的問題,比如在firefox里,document.stylesheets的cssrules屬性會被拒絕訪問。
新聞熱點
疑難解答
圖片精選