一、發(fā)現(xiàn)黑科技的起因
今天在微信公眾號(hào)看到一篇技術(shù)博文,想用印象筆記收藏,所以發(fā)送了文章鏈接到pc上。然后習(xí)慣性地打開(kāi)控制臺(tái),看看源碼,想了解下最近微信用了什么新技術(shù)。
呵呵,以下勾起了我偵探的欲望。頁(yè)面加載后的異常點(diǎn)就是只加載了一個(gè)js,如下圖所示:

我很詫異,為什么已經(jīng)開(kāi)啟了Disable cache,js只加載了一個(gè),而且體積這么小。接著,我按住Ctrl+O進(jìn)行資源文件查找,發(fā)現(xiàn)我被“忽悠”了。其實(shí)根本就不止一個(gè)js文件。

腦袋里靈光一閃,不會(huì)是用localStorage做了緩存吧?!趕緊看了下localStronge,還真是。。。。

心里一陣澎湃,這不是我之前就想實(shí)現(xiàn)的加載性能優(yōu)化的想法嗎!乖乖,我孤陋寡聞了,已經(jīng)有前端團(tuán)隊(duì)實(shí)現(xiàn)了代碼。
二、談?wù)勎募虞d方面的優(yōu)化思路
通常,前端的資源文件加載優(yōu)化,就是在文件不修改迭代的情況下,盡可能多地利用緩存,避免多次下載同樣的文件。
一般的做法就是盡量延長(zhǎng)資源的有效期,也就是設(shè)置 Cache-Control里的max-age,使頁(yè)面資源請(qǐng)求的返回碼為304,讓瀏覽器直接使用本地緩存。
雖然pc端的協(xié)商緩存(304)很快,但手機(jī)端因?yàn)榫W(wǎng)絡(luò)原因,協(xié)商緩存的效果就沒(méi)pc端那么好了。而且,手機(jī)會(huì)經(jīng)常清除本地緩存,所以文件緩存的時(shí)間也不會(huì)很長(zhǎng)。
這個(gè)時(shí)候,localStorage就派上用場(chǎng)了。
localStorage相比cookie,可以緩存大體積的數(shù)據(jù),而且是永久有效。所以,如果把js資源和css資源存儲(chǔ)在localStorage中,則可以省去發(fā)送http請(qǐng)求所消耗的時(shí)間,大大提高用戶(hù)的瀏覽體驗(yàn)。
三、用localStorage做資源緩存需要解決的問(wèn)題
3.1 版本更新機(jī)制
只要一個(gè)項(xiàng)目還在迭代開(kāi)發(fā),就難以避免需要更新資源文件。
普通的資源請(qǐng)求,可以根據(jù)
文件名+md5 http://res.wx.qq.com/mmbizwap/zh_CN/htmledition/js/biz_wap/moon32ebc4.js
或者
在資源鏈接后面加上特定的后綴http://1.ss.faisys.com/js/comm/fai.min.js?v=201612051739
做標(biāo)識(shí)來(lái)判斷是否需要更新資源。
如果用localStorage做,則需要一套新的緩存更新機(jī)制。
3.2 搭建更新代碼的腳手架
使用localStorage緩存,則需要一個(gè)新的腳手架來(lái)管理資源文件的讀取和寫(xiě)入。
3.3 后臺(tái)輸出一份資源配置信息
因?yàn)樾枰岸俗鲑Y源更新,所以后臺(tái)要輸出一份依據(jù)給前端做判斷用,也就是需要一份資源配置信息。前端根據(jù)配置信息,進(jìn)行匹配和比較,最終決定 使用localStorage緩存,還是重新發(fā)起請(qǐng)求,下載最新的資源文件。
3.4 存在XSS安全隱患
localStorage中的信息,客戶(hù)端是可以任意修改的。如果哪個(gè)黑客想練手一下,可以任意注入js代碼。那么,在頁(yè)面刷新的時(shí)候,注入的代碼也將會(huì)被執(zhí)行。
四、微信的做法解析
4.1 版本標(biāo)識(shí)

以__MOON__a/a_report.js為例,版本信息用key __MOON__a/a_report.js_ver存儲(chǔ),存儲(chǔ)的value為//res.wx.qq.com/mmbizwap/zh_CN/htmledition/js/a/a_report32e586.js。
如果按普通加載方式,直接將該value取出來(lái),設(shè)置到script節(jié)點(diǎn)的src屬性,即可完成加載。
微信判斷該版本是否最新,就是用該value值與后臺(tái)輸出的配置信息進(jìn)行比較,最后得出是否更新的結(jié)果。
如果value值與配置信息一致,則使用緩存。否則,重新發(fā)起請(qǐng)求加載。
4.2 腳手架
可以看出,微信使用的是自己開(kāi)發(fā)的腳手架moon.js,在這個(gè)網(wǎng)頁(yè)中的實(shí)際文件名是moon32ebc4.js。
因?yàn)槭腔煜^(guò)變量名的文件,所以要看出具體代碼的走向,有點(diǎn)費(fèi)勁,這里就不做分析了。
4.3 資源配置信息
因?yàn)槟_手架moon.js需要資源配置信息才能正常工作,所以配置信息一定會(huì)在moon.js之前輸出。
依次查看moon.js之前的script標(biāo)簽,發(fā)現(xiàn)了window.moon_map這個(gè)json對(duì)象。

利用控制臺(tái)輸出該變量查看信息如下:

看到這里,可以明確一個(gè)點(diǎn):這就是更新機(jī)制所必備的資源配置信息表了。
而且,可以看出,該配置信息json對(duì)象的key,就對(duì)應(yīng)localStorage中的key。同理,value值也是一一對(duì)應(yīng)。
4.4 XSS攻擊
此處是為了驗(yàn)證微信的緩存機(jī)制是否存在XSS攻擊,看到這里的童鞋可千萬(wàn)不要去做壞事。
我在一個(gè)js緩存代碼中,插入alert("hehe");,看頁(yè)面刷新的時(shí)候,是否會(huì)出現(xiàn)該彈窗,來(lái)驗(yàn)證是否存在攻擊漏洞。

刷新頁(yè)面后,結(jié)果如下圖:

可以看出,微信也沒(méi)有解決這類(lèi)問(wèn)題。所以,這種緩存機(jī)制,還是有先天不足的。
4.5 測(cè)試微信的更新機(jī)制
修改localStorage中 key __MOON__a/a_report.js_ver對(duì)應(yīng)的value值,讓微信的腳手架moon.js更新__MOON__a/a_report.js,刷掉我剛才主動(dòng)插入的代碼。
這里,我修改文件名為***587.js(原來(lái)的文件名為***586.js)。接著F5刷新頁(yè)面。
結(jié)果為:report.js代碼更新了,版本號(hào)也恢復(fù)回 ***586.js。

五、結(jié)論
localStorage緩存有其用武之地,但不是萬(wàn)能的。需要注意以上提及的坑。
可以應(yīng)用的場(chǎng)景我歸納為以下幾點(diǎn):
1. 非首屏渲染需要的css文件,可以做LS緩存。
首屏渲染需要的css,需要按常規(guī)方式輸出,因?yàn)镾EO需要,不然爬蟲(chóng)爬取頁(yè)面的時(shí)候,頁(yè)面效果會(huì)很不好。而非首屏的css,則可以用LS緩存,減少資源下載時(shí)間。
2. 展示類(lèi)、動(dòng)畫(huà)類(lèi)等非業(yè)務(wù)主要邏輯的代碼,可以做LS緩存。
這樣,可以一定程度上避免業(yè)務(wù)層的安全漏洞。當(dāng)然,前端再怎么做防護(hù)都是一層薄紙。重要的,還是后臺(tái)接口要做好安全保護(hù)。
3. 移動(dòng)端可以做LS緩存。PC端做LS緩存,起到的優(yōu)化作用不大。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持武林網(wǎng)!
 
  | 
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注