国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

高性能的網(wǎng)頁(yè)開發(fā)概要

bovenson / 2957人閱讀

摘要:或者說一直以來我是缺乏開發(fā)高性能網(wǎng)頁(yè)的意識(shí)的,但是想做一個(gè)好的前端開發(fā)者,是需要在當(dāng)自己編寫的程序慢慢復(fù)雜以后還能繼續(xù)保持網(wǎng)頁(yè)的高性能的。

不知道有多少人和我一樣,在以前的開發(fā)過程中很少在乎自己編寫的網(wǎng)頁(yè)的性能。或者說一直以來我是缺乏開發(fā)高性能網(wǎng)頁(yè)的意識(shí)的,但是想做一個(gè)好的前端開發(fā)者,是需要在當(dāng)自己編寫的程序慢慢復(fù)雜以后還能繼續(xù)保持網(wǎng)頁(yè)的高性能的。這需要我們對(duì)JavaScript語句,對(duì)其運(yùn)行的宿主環(huán)境(瀏覽器等),對(duì)它的操作對(duì)象(DOM等)有更深入的理解。

什么樣的網(wǎng)頁(yè)是高性能的網(wǎng)頁(yè)?
我想一個(gè)網(wǎng)頁(yè)是否高性能主要體現(xiàn)在兩個(gè)方面,一是網(wǎng)頁(yè)中代碼真實(shí)的運(yùn)行速度,二是用戶在使用時(shí)感受到的速度,我們一項(xiàng)項(xiàng)的來討論。

提高代碼執(zhí)行的效率

想要提高代碼的執(zhí)行效率,我們首先得知道我們使用JS做不同的事情時(shí),其執(zhí)行效率各是如何。一般說來,web前端開發(fā)中我們常做的操作主要是數(shù)據(jù)獲取和存儲(chǔ)操作DOM,除此之外,我們知道JS中達(dá)到同一目的可能會(huì)有多種途徑,但其實(shí)各種途徑執(zhí)行效率并不相同,我們應(yīng)該選擇最合適的途徑

數(shù)據(jù)存儲(chǔ)和訪問

先說數(shù)據(jù)存儲(chǔ),計(jì)算機(jī)中,數(shù)據(jù)肯定是存在于內(nèi)存之中,但是訪問到具體內(nèi)存所在的位置卻有不同的方法,從這個(gè)角度看JS中有四種基本的數(shù)據(jù)存取位置

- 字面量:只代表自身,不存儲(chǔ)在特定位置
* 本地變量:使用關(guān)鍵字var 存儲(chǔ)的數(shù)據(jù)存儲(chǔ)單元
* 數(shù)組元素:存儲(chǔ)在JavaScript的數(shù)組對(duì)象內(nèi)部,以數(shù)字為索引
* 對(duì)象成員:存儲(chǔ)在JavaScript對(duì)象內(nèi)部,以字符串為索引

不同存儲(chǔ)方式的訪問速度
其實(shí)很容易就可以理解,訪問一個(gè)數(shù)據(jù)所經(jīng)歷的層級(jí)越少,其訪問速度越快,這樣看來訪問字面量和局部變量速度最快,而訪問數(shù)組元素和對(duì)象成員相對(duì)較慢;

從數(shù)據(jù)存儲(chǔ)和訪問角度來看,提升效率的核心在于存儲(chǔ)和訪問要直接,不要拐彎抹角。我們以原型鏈作用域為例來說明如何優(yōu)化。

原型鏈
對(duì)象的原型決定了實(shí)例的類型,原型鏈可以很長(zhǎng),實(shí)例所調(diào)用的方法在原型鏈層級(jí)中越深則效率越低。因此也許需要我們保證原型鏈不要太長(zhǎng),對(duì)于經(jīng)常需要使用到的方法或?qū)傩裕M量保證它在原型鏈的淺層。

作用域(鏈)
作用域也是JavaScript中一個(gè)重要的概念,一般說來變量在作用域中的位置越深,訪問所需的時(shí)間就越長(zhǎng)。
局部變量存在于作用域鏈的起始位置,其訪問速度比訪問跨作用域變量快,而全局變量處于作用域鏈的最末端,其訪問速度也是最慢的。
一般說來JavaScript的詞法作用域在代碼編譯階段就已經(jīng)確定,這種確定性帶來的好處是,代碼在執(zhí)行過程中,能夠預(yù)測(cè)如何對(duì)變量進(jìn)行查找,從而提高代碼運(yùn)行階段的執(zhí)行效率。我們也知道JavaScript中有一些語句是會(huì)臨時(shí)改變作用域的,比如說witheval,try...catch...中的catch子句,使用它們會(huì)破壞已有的確定性,從而降低代碼執(zhí)行效率,因此這些語句應(yīng)該小心使用。

從數(shù)據(jù)的存儲(chǔ)和訪問角度,可以從以下角度進(jìn)行優(yōu)化:

- 我們應(yīng)該盡量少用嵌套的對(duì)象成員,多層嵌套時(shí)會(huì)明顯影響性能,如果實(shí)在要使用(尤其是多次使用);
- 我們可以通過把常用的對(duì)象成員,數(shù)組元素,跨域變量保存在局部變量中,再使用來改善JavaScript性能。
DOM編程

通過DOM API,利用JavaScript我們有了操作網(wǎng)頁(yè)上的元素的能力,這也使得我們的網(wǎng)頁(yè)活靈活現(xiàn)。可是遺憾的是DOM編程是性能消耗大戶,它天生就慢,究其原因,是因?yàn)樵跒g覽器中DOM渲染和JavaScript引擎是獨(dú)立實(shí)現(xiàn)的,不同的瀏覽器實(shí)現(xiàn)機(jī)制不同,具體可見下表。

類別 IE Safari Chrome Firefox
JS引擎 jscript.dll JavaScriptCore V8 SpiderMonkey(TraceMonkey)
DOM和渲染 mshtml.dll (Trident) Webkit中的WebCore Webkit中的WebCore Gecko

DOM渲染和JavaScript引擎的獨(dú)立實(shí)現(xiàn)意味著這兩個(gè)功能好像位于一條大河的兩岸,二者的每次交互都要渡過這條河,這很明顯會(huì)產(chǎn)生不少額外的性能消耗,這也是我們常說操作DOM是非常消耗性能的原因。
從這個(gè)角度我們想到,想要減少DOM操作帶來的性能的消耗,核心我們要做的是減少訪問和修改等DOM操作。
我們可以從以下幾方面著手DOM編程的優(yōu)化:

把運(yùn)算盡量留在ECMAScript這一端處理,在實(shí)際開發(fā)過程中我們應(yīng)該對(duì)DOM不做非必要的操作,在獲得必要的值以后,可以把這些值存儲(chǔ)在局部變量之中,純使用JS對(duì)該值進(jìn)行相關(guān)運(yùn)算,直接用最后的結(jié)果來修改DOM元素,可能這么說來并不是很直觀,但是回頭看看我們的項(xiàng)目,我們有沒有過在循環(huán)或者會(huì)多次使用的函數(shù)中,每次都重新獲取某個(gè)不變的DOM相關(guān)的值;

小心處理HTML集合:

HTML集合是包含了 DOM節(jié)點(diǎn)引用的類數(shù)組對(duì)象,類似于以下方法,獲取的都是這種HTML集合:
document.getElementsByName()
document.getElementByClassName();
document.getElementByTagName();
這種類數(shù)組對(duì)象具備普通數(shù)組的一些特點(diǎn),比如擁有length屬性,可以通過數(shù)字索引的方式訪問到對(duì)應(yīng)的對(duì)象,但是卻并沒有數(shù)組的pushslice等方法,重點(diǎn)在于在使用過程中這個(gè)集合實(shí)時(shí)連系著底層的文檔,每次訪問這種HTML集合,都會(huì)重復(fù)執(zhí)行查詢的過程,造成較大的性能消耗;下面是一個(gè)典型的多次訪問的例子:

    var alldivs = document.getElementsByTagName("div");
    for(var i = 0;i

上述代碼是一個(gè)死循環(huán),在每次循環(huán)的時(shí)候alldivs都會(huì)重新遍歷DOM,獲得更新,產(chǎn)生了不必要的性能消耗;
合理的使用方式應(yīng)該是,把集合的長(zhǎng)度緩存到一個(gè)變量中,在迭代中使用這個(gè)局部變量。如果需要經(jīng)常操作集合元素,我們可以把這個(gè)集合拷貝到一個(gè)數(shù)組中,操作數(shù)組比操作HTML集合性能高;使用下述方法可以把一個(gè)HTML集合拷貝為普通的數(shù)組:

// 拷貝函數(shù)
function toArray(coll){
    for(var i=0,a=[],len=coll.length;i

JS代碼的運(yùn)行需要宿主環(huán)境,就web前端來說,這個(gè)環(huán)境就是我們的瀏覽器,一般說來瀏覽器會(huì)對(duì)一些常見操作進(jìn)行一定的優(yōu)化,使用優(yōu)化后的API,性能更高,我們應(yīng)該盡量使用那這些優(yōu)化過的API,對(duì)現(xiàn)代瀏覽器中的DOM操作來說,有以下一些優(yōu)化后的API:

優(yōu)化后 原始
children childnodes
childElementCount children.length
firstElementChild firstChild
lastElementChild lastChild
nextElementSibling nextSibling
previousElementSibling previousSibling
document.querySelector() document.getElemnetByClassName…

減少重繪與重排
重排和重繪也是大家經(jīng)常聽到的概念:

重排:DOM變化影響了元素的幾何屬性(比如改變邊框?qū)挾龋鹌渌氐奈恢靡惨虼耸艿接绊懀瑸g覽器會(huì)使得渲染樹中受到影響的部分失效,并重新構(gòu)建渲染樹;
重繪:重排完成后,瀏覽器會(huì)重新繪制受影響的部分到屏幕中(并不是所有的DOM變化都會(huì)影響幾何屬性,例如改變一個(gè)元素的背景色并不會(huì)影響它的寬和高);

重排和重繪都是代價(jià)很高的操作,會(huì)直接導(dǎo)致Web應(yīng)用程序的UI反應(yīng)遲鈍,應(yīng)該盡量減少這類過程的發(fā)生。一般說來下面的操作會(huì)導(dǎo)致重排:

- 添加刪除可見的DOM元素;
* 元素位置改變;
* 元素尺寸改變(`padding,margin,border,width,height...`)
* 內(nèi)容改變;(文本內(nèi)容,或圖片被另外一個(gè)不同尺寸的圖片替代);
* 頁(yè)面渲染器初始化;
* 瀏覽器窗口尺寸改變;

每次重排都會(huì)產(chǎn)生計(jì)算消耗,大多數(shù)瀏覽器會(huì)通過隊(duì)列化修改,批量執(zhí)行來優(yōu)化重排過程,減少性能消耗。但是也有部分操作會(huì)強(qiáng)制刷新隊(duì)列,要求重排任務(wù)立即執(zhí)行。如下:

- `offsrtTop`,`offsetLeft`,`offsetWidth`,`offsetHeight`;
* `scrollTop`,`scrollLeft`,`scrollWidth`,`scrollHeight`;
* `clientTop`,`clientLeft`,`clientWidth`,`clientHeight`;
* `getComputedStyle()`

上述操作都要求返回最新的布局信息,瀏覽器不得不執(zhí)行渲染隊(duì)列中待處理的任務(wù),觸發(fā)重排返回正確的值。通過緩存布局信息可以減少重排次數(shù),這一點(diǎn)是我一直忽略的一點(diǎn),曾經(jīng)多次在循環(huán)或多次調(diào)用的函數(shù)中直接使用上述操作獲取距離;

總的來說最小化重繪和重排的核心是合并多次對(duì)DOM和樣式的修改,然后一次處理掉,主要有以下幾種方法:

- 使用cssText屬性(適用于動(dòng)態(tài)改變)
var el = document.getElementById(‘mydiv’);
// 下面這種寫法可以覆蓋已存在的樣式信息
el.style.cssText = ‘border-left:1px;border-right:2px;padding:5px’;
// 下面這種寫法可以把新屬性附加在cssTest字符串后面
el.style.cssText += ‘;border-left:1px;’;
- 對(duì)于不依賴于運(yùn)行邏輯和計(jì)算的情況,直接改變CSS的`class`名稱是一種比較好的選擇
- 批量修改DOM:操作如下
    - 使元素脫離文檔流;
        * 隱藏元素,應(yīng)用修改,重新顯示;
        * 使用文檔片段,在當(dāng)前DOM外構(gòu)建一個(gè)子樹,再把它拷貝到文檔;
        * 將原始元素拷貝到一個(gè)脫離文檔的節(jié)點(diǎn)中,修改副本,完成后替換原始元素;
    * 對(duì)其應(yīng)用多重改變;
    * 把元素帶回文檔中;
在這個(gè)過程中只有第一步和第三步會(huì)觸發(fā)重排,可參加下述代碼。
var fragment = document.createDocumentFragment();//一個(gè)輕量級(jí)的document對(duì)象
// 組裝fragment...(省略)
// 加入文檔中
document.getElementById(‘mylist’).appendChild(fragment);

var old = document.getElementById(‘mylist’);
var clone = old.cloneNode(true);
// 對(duì)clone進(jìn)行處理
old.parentNode.replaceChild(clone,old);
- 對(duì)動(dòng)畫元素可進(jìn)行如下優(yōu)化
    - 對(duì)動(dòng)畫元素使用絕對(duì)定位,將其脫離文檔流;
    * 讓元素動(dòng)起來,懂的時(shí)候會(huì)臨時(shí)覆蓋部分頁(yè)面(不會(huì)產(chǎn)生重排并繪制頁(yè)面的大部分內(nèi)容);
    * 當(dāng)動(dòng)畫結(jié)束時(shí)恢復(fù)定位,從而只會(huì)下移一次文檔的其它元素;

此外如果頁(yè)面中對(duì)多個(gè)元素綁定事件處理器,也是會(huì)影響性能的,事件綁定會(huì)占用處理時(shí)間,瀏覽器也需要跟蹤每個(gè)事件處理器,會(huì)占用更多的內(nèi)存。針對(duì)這種情況我們可以采用使用事件委托機(jī)制:也就是說把事件綁定在頂層父元素,通過e.target獲取點(diǎn)擊的元素,判斷是否是希望點(diǎn)擊的對(duì)象,執(zhí)行對(duì)應(yīng)的操作,幸運(yùn)的是現(xiàn)在的很多框架已經(jīng)幫我們做了這一點(diǎn)(React中的事件系統(tǒng)就用到了事件委托機(jī)制)。

選擇合適的JavaScript語句

殊途同歸付出的代價(jià)卻不一樣,和任何編程語言一樣,代碼的寫法和算法會(huì)影響運(yùn)行時(shí)間。而我們寫的最多的語句是迭代判斷

選擇合適的迭代語句

JavaScript提供了多種迭代方法:

四種循環(huán)類型:

for;

while;

do…while循環(huán);

for…in…循環(huán);

就四種循環(huán)類型而言,前三種循環(huán)性能相當(dāng),第四種for...in...用以枚舉任意對(duì)象的屬性名,所枚舉的屬性包括對(duì)象實(shí)例屬性及從原型鏈中繼承來的屬性,由于涉及到了原型鏈,其執(zhí)行效率明顯慢于前三種循環(huán)。就前三種而言影響性能的主要因素不在于選擇哪種循環(huán)類型,而在于每次迭代處理的事務(wù)迭代的次數(shù)

減少每次迭代的工作量
很明顯,達(dá)到同樣的目的,每次迭代處理的事務(wù)越少,效率越高。舉例來說

// 普通循環(huán)體
for(var i=0;i

普通循環(huán)體每次循環(huán)都會(huì)有以下操作:

1. 在控制條件中查找一次屬性(`items.length`);
2. 在控制條件中執(zhí)行一次數(shù)值比較(`i

優(yōu)化后的循環(huán)體每次迭代會(huì)有以下操作:

1. 一次控制條件中的比較(`i==true`);
2. 一次減法操作(`i—`);
3. 一次數(shù)組查找(`item[i]`);
4. 一次函數(shù)調(diào)用(`process(item[i])`);

明顯優(yōu)化后的迭代操作步驟更少,如果迭代次數(shù)很多,多次這樣小的優(yōu)化就能節(jié)省不錯(cuò)的性能,如果process()函數(shù)本身也充滿了各種小優(yōu)化,性能的提升還是很可觀的;

減少迭代次數(shù)
每次迭代其實(shí)是需要付出額外的性能消耗的,可以使用Duff’s Device方法減少迭代次數(shù),方法如下;

    // credit:Jeff Greenberg
var i = items.length%8;
whild(i){
    process(items[i--]);
}

i = Math.floor(items.length/8);

// 如果循環(huán)次數(shù)超過1000,與常規(guī)循環(huán)結(jié)構(gòu)相比,其效率會(huì)大幅度提高
while(i){
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
}

‘Duff’s Device’循環(huán)體展開技術(shù),使得一次迭代實(shí)際上執(zhí)行了多次迭代的操作。如果迭代次數(shù)大于1000次,與常規(guī)循環(huán)結(jié)構(gòu)相比,就會(huì)有可見的性能提升。

基于函數(shù)的迭代

forEach();

map();

every();

等…

基于函數(shù)的迭代是一個(gè)非常便利的迭代方法,但它比基于循環(huán)的迭代要慢一些。每個(gè)數(shù)組項(xiàng)都需要調(diào)用外部方法所帶來的開銷是速度慢的主要原因。經(jīng)測(cè)試基于函數(shù)的迭代比基于循環(huán)的迭代慢八倍,選擇便利還是性能這就是我們自己的選擇了;

選擇合適的條件語句

條件表達(dá)式?jīng)Q定了JavaScript運(yùn)行流的走向,常見的條件語句有if…else...switch...case...兩種:
switch語句的實(shí)現(xiàn)采用了branch table(分支表)索引進(jìn)行優(yōu)化,這使得switch...case是比if-else快的,但是只有條件數(shù)量很大時(shí)才快的明顯。這兩個(gè)語句的主要性能區(qū)別是,當(dāng)條件增加時(shí),if...else性能負(fù)擔(dān)增加的程度比switch大。具體選用那個(gè),還是應(yīng)該依據(jù)條件數(shù)量來判斷。

如果選用if...else,也有優(yōu)化方法,一是把最可能出現(xiàn)的條件放在最前面,二是可以通過嵌套的if...else語句減少查詢時(shí)間;

在JavaScript中還可以通過查找表的方式來獲取滿足某條件的結(jié)果。當(dāng)有大量離散值需要測(cè)試時(shí),if...elseswitch比使用查找表慢很多。查找表可以通過數(shù)組模擬使用方法如下:

var results = [result0,result1,result2,result3,result4,result5,result6,result7,result8,result9];

return results[value];

從語言本身的角度來看,做到上述各點(diǎn),我們的代碼性能就會(huì)有比較大的提升了,但是代碼性能的提升,只是代碼執(zhí)行本身變快了,并不一定是用戶體驗(yàn)到的時(shí)間變短了,一個(gè)好的web網(wǎng)頁(yè),還應(yīng)該讓用戶感覺到我們的網(wǎng)頁(yè)很快。

更好的交互體驗(yàn)

人類對(duì)時(shí)間的感覺其實(shí)是不準(zhǔn)確的,考慮兩種場(chǎng)景,

一種場(chǎng)景是,一段代碼執(zhí)行10s,10s后所有內(nèi)容一下子全部顯示出來;
另外一種場(chǎng)景是,總共需要執(zhí)行12s,但是每秒都在頁(yè)面上添加一些內(nèi)容,主體內(nèi)容先添加,次要內(nèi)容后添加;

上述兩種場(chǎng)景給人的感覺完全不一樣。大部分人會(huì)覺得后面那個(gè)12s時(shí)間更短。

用戶感覺到的時(shí)間的長(zhǎng)短取決于用戶與網(wǎng)頁(yè)交互時(shí)收到的反饋,一般說來100ms是個(gè)時(shí)間節(jié)點(diǎn),如果界面沒在100ms內(nèi)對(duì)用戶的行為作出反饋,用戶就會(huì)有卡頓的感覺,開發(fā)過手機(jī)版的網(wǎng)頁(yè)的童鞋都知道,當(dāng)你觸發(fā)手機(jī)端默認(rèn)的onClick事件時(shí),有些瀏覽器出于要確認(rèn)你是否想要雙擊,會(huì)在單擊事件觸發(fā)300ms后,才執(zhí)行相應(yīng)的操作,用戶在這時(shí)候會(huì)有明顯卡頓的感覺的。為了達(dá)到更好的交互體驗(yàn),我們可以從腳本加載時(shí)機(jī),不阻塞瀏覽器的UI線程,高效使用Ajax,合理構(gòu)建和部署JavaScript應(yīng)用等方面進(jìn)行優(yōu)化:

加載和執(zhí)行

我們都知道多數(shù)瀏覽器使用單一進(jìn)程來處理UI刷新和JavaScript腳本執(zhí)行,當(dāng)執(zhí)行較大的JavaScript腳本時(shí)會(huì)阻塞UI的刷新。這是一種非常不好的體驗(yàn)。所有在頁(yè)面初始化時(shí)期,我們都會(huì)把JavaScript放在標(biāo)簽之前。我們也可以采用動(dòng)態(tài)加載的方式,無論在何時(shí)啟動(dòng),文件的下載和執(zhí)行過程都不會(huì)阻塞頁(yè)面其他進(jìn)程,一種常見的動(dòng)態(tài)加載方式如下,當(dāng)然我們也可以使用AJAX進(jìn)行動(dòng)態(tài)加載。

    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src="file1.js";
    document.getElementsByTagName("head")[0].appendChild(script);
    // 無論在何時(shí)啟動(dòng)下載,文件的下載和執(zhí)行過程不會(huì)阻塞頁(yè)面其他進(jìn)程
    // 通過檢測(cè)load事件可以獲得腳本加載完成時(shí)的狀態(tài)
構(gòu)建快速響應(yīng)的用戶界面

我們已經(jīng)知道,當(dāng)JavaScript代碼執(zhí)行時(shí),用戶界面其實(shí)是屬于鎖定狀態(tài)的,管理好JavaScript的運(yùn)行時(shí)間對(duì)Web應(yīng)用的性能至關(guān)重要。

用來執(zhí)行JavaScript和更新用戶界面的進(jìn)程通常稱為“瀏覽器UI線程”。其工作基于一個(gè)簡(jiǎn)單的任務(wù)隊(duì)列系統(tǒng),具體原理是所有的前端任務(wù)會(huì)被保存在任務(wù)隊(duì)列中直到進(jìn)程空閑,一旦空閑,隊(duì)列中的下一個(gè)任務(wù)就會(huì)被提取出來并運(yùn)行。這些任務(wù)可能是一段待執(zhí)行的JavaScript代碼,UI更新(重排重繪等)。
一般瀏覽器有自己的JavaScript長(zhǎng)時(shí)間運(yùn)行限制,不同瀏覽器限制的時(shí)間不同,有的是5~10s,有的是運(yùn)行超過500萬行語句時(shí)提醒;達(dá)到限制時(shí)間后對(duì)頁(yè)面的處理方式也不同,有的直接導(dǎo)致瀏覽器崩潰,有的會(huì)發(fā)出明顯可見的警告信息。

前面我們已經(jīng)討論過如果等待時(shí)間不超過100ms,一般人就會(huì)覺得操作是連貫的。這給我們提供了一個(gè)思路,我們的一段JavaScript代碼的執(zhí)行時(shí)間如果不超過100ms,操作起來感覺就是連貫的。

通過分隔JavaScript代碼的執(zhí)行,我們可以讓JavaScript的執(zhí)行時(shí)間不超過100ms,主要途徑主要有兩種:
使用定時(shí)器讓出時(shí)間片段使用web worker

通過定時(shí)器是分割UI線程的時(shí)間片段

JavaScript中有兩種不同的定時(shí)器setTimeout()setInterval(),它們都接受兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)函數(shù),第二個(gè)參數(shù)是一段時(shí)間。當(dāng)定時(shí)器被調(diào)用時(shí),它們會(huì)告訴JavaScript引擎在等待我們所定的時(shí)間后,添加一個(gè)JavaScript任務(wù)到UI線程中。
定時(shí)器的意義在于,每當(dāng)我們創(chuàng)建一個(gè)定時(shí)器,UI線程就會(huì)暫停,并從一個(gè)任務(wù)切換到下一個(gè)任務(wù),定時(shí)器也會(huì)重置所有相關(guān)的瀏覽器限制,包括長(zhǎng)時(shí)間運(yùn)行腳本限制,調(diào)用棧的限制。

我們也應(yīng)該明確,定時(shí)器的延遲時(shí)間并非總是精確的,每次相差大約幾毫秒,所以定時(shí)器不太適合拿來計(jì)時(shí)(比如說在windows系統(tǒng)中定時(shí)器分辨率為15ms),一般建議的延遲的最小值為25ms,以確保至少有15ms的延遲。

我們通過下面的代碼看看如何利用定時(shí)器無阻塞的處理大數(shù)組

var todo = items.concat();//items是原始的大數(shù)組

setTimeout(function(){
    // 取出數(shù)組的下個(gè)元素并進(jìn)行處理
    process(todo.shift());

    // 如果還有需要處理的元素,創(chuàng)建另一個(gè)定時(shí)器,再25ms執(zhí)行一次
    if(todo.length>0){
        // arguments.callee指向當(dāng)前正在運(yùn)行的匿名函數(shù)
        setTimeout(arguments.callee,25);
    }else{
        // 如果所有的數(shù)組都被處理了,執(zhí)行callback()函數(shù)
        callback(items);
    }
},25);

利用定時(shí)器可以按照下述方法分割運(yùn)行時(shí)間過長(zhǎng)的函數(shù)

//原函數(shù)
function saveDocument(id){
    //保存文檔
    openDocument(id);
    writeText(id);
    closeDocument(id);

    //將信息更新到界面
    updateUI(id);
}

// 使用定時(shí)器分割任務(wù)的方法
function saveDocument(id){
    var tasks=[openDocument,writeText,closeDocument,updateUI];

    setTimeout(function(){
        var task = tasks.shift();
        task(id);

        if(tasks.length>0){
            setTimeout(arguments.callee,25);
        }
    },25);
}

定時(shí)器把我們的任務(wù)分解成了一個(gè)個(gè)的不致于阻塞的片段,雖然總的執(zhí)行時(shí)間可能是變長(zhǎng)了,但是會(huì)讓我們的交互體驗(yàn)發(fā)生翻天覆地的變化,使用得當(dāng),我們可以避免大部分完全卡住的情況了。不過定時(shí)器的使用也有一個(gè)限制,我們應(yīng)該保證同一時(shí)間只有一個(gè)定時(shí)器的存在,防止因?yàn)槎〞r(shí)器使用過度導(dǎo)致的性能問題。

WebWorker給Web應(yīng)用帶來的巨大性能潛力提升

WebWorker是HTML5新提供的一組API,它引入了一個(gè)接口,能使代碼運(yùn)行且不占用瀏覽器UI線程的時(shí)間,這里只做簡(jiǎn)單介紹,具體使用請(qǐng)查看相關(guān)文檔:
一個(gè)WebWorker其實(shí)是JavaScript特性的一個(gè)子集,其運(yùn)行環(huán)境由以下部分組成:

- 一個(gè)`navigator`對(duì)象,包含四個(gè)屬性:`appName`,`appVeision`,`userAgent,platform`;
* 一個(gè)`location`對(duì)象(與`window.location`相同,不過所有屬性都是只讀的);
* 一個(gè)`self`對(duì)象,指向全局`worker`對(duì)象;
* 一個(gè)`importScript()`方法,用來加載`Worker`所用到的外部JavaScript文件;
* 所有的ECMAScript對(duì)象,諸如:`Object,Array,Date`等;
* `XMLHttpRequest`構(gòu)造器;
* `setTimeout()`和`setInterval()`方法;
* 一個(gè)`close()`方法,它可以立即停止Worker運(yùn)行;

主網(wǎng)頁(yè)與Worker相互通信的方法
Worker與網(wǎng)頁(yè)代碼通過事件接口進(jìn)行通信。主網(wǎng)頁(yè)和Worker都會(huì)通過postMessage()傳遞數(shù)據(jù)給對(duì)方;使用onmessage事件處理器來接收來自對(duì)方的信息;

// 主頁(yè)面
var worker = new Worker("code.js");
worker.onmessage = function(event){
    alert(event.data);
};
worker.postMessage("Nicholas");

//code.js內(nèi)部的代碼
self.onmessage = function(event){
    self.postMessage("Hello,"+ event.data+"!");
}

相互傳遞的原始值可以是字符串,數(shù)字,布爾值,nullundefined,也可以ObjectArray的實(shí)例。

加載外部文件的方法
在一個(gè)web worker中可以使用importScript()方法加載外部JavaScript文件,該方法接收一個(gè)或多個(gè)url作為參數(shù),調(diào)用過程是阻塞式的,所有文件加載并執(zhí)行完成以后,腳本才會(huì)繼續(xù)運(yùn)行。但并不會(huì)影響UI響應(yīng)。
加載允許異步發(fā)送和接收數(shù)據(jù)。可以在請(qǐng)求中添加任何頭信息和參數(shù),并讀取服務(wù)器返回的所有頭信息,以及響應(yīng)文本。
importScripts(‘file1.js’,’file2.js’);

WebWorker的實(shí)際應(yīng)用

- 適合處理純數(shù)據(jù),或者與UI無關(guān)的長(zhǎng)時(shí)間運(yùn)行腳本;
* 編碼,解碼大字符串;
* 復(fù)雜數(shù)學(xué)運(yùn)算;
* 大數(shù)組排序;

總的說來,Web應(yīng)用越復(fù)雜,積極主動(dòng)管理UI線程越重要,復(fù)雜的前端應(yīng)用更應(yīng)該合理使用定時(shí)器分割任務(wù)或使用WebWorker進(jìn)行額外計(jì)算從而不影響用戶體驗(yàn)。

使用Ajax

Ajax是高性能JavaScript的基礎(chǔ),它通過異步的方式在客戶端和服務(wù)器端之間傳輸數(shù)據(jù),避免頁(yè)面資源一窩蜂的下載,從而起到防止阻塞,提高用戶體驗(yàn)的效果,在使用時(shí)我們應(yīng)該選擇最合適的傳輸方式和最有效的數(shù)據(jù)格式

傳輸方式

數(shù)據(jù)請(qǐng)求方式
異步從服務(wù)器端獲取數(shù)據(jù)有多種方法,常用的請(qǐng)求有以下三種:

XHR
這是目前最常用的技術(shù),可以在請(qǐng)求中添加任何頭信息和參數(shù),讀取服務(wù)器返回的所有頭信息以及響應(yīng)文本。

這種方法的缺點(diǎn)在于不能使用XHR從外域請(qǐng)求數(shù)據(jù),從服務(wù)器傳回的數(shù)據(jù)會(huì)被當(dāng)作字符串或者XML對(duì)象,處理大量數(shù)據(jù)時(shí)會(huì)很慢。經(jīng)GET請(qǐng)求的數(shù)據(jù)會(huì)被緩存起來,如果需要多次請(qǐng)求同一數(shù)據(jù)的話,使用GET請(qǐng)求有助于提升性能,當(dāng)請(qǐng)求的URL加上參數(shù)的長(zhǎng)度接近或超過2048個(gè)字符時(shí),才應(yīng)該用POST獲取數(shù)據(jù)。

XHR的使用可見以下示例:

var url = ‘/data.php’;
var params = [
    ‘id=123123’,
    ‘limit = 20’,
];

var req = new XMLHttpRequest();

req.onreadystatechange = function(){
    if(req.readyState===4){
        var responseHeader = req.getAllResponseHeaders(); // 獲取響應(yīng)頭信息
        var data = req.responseText; // 獲取數(shù)據(jù)
        // 數(shù)據(jù)處理相關(guān)程序
    }
}

req.open(‘GET’,url+’?’+params.join(‘&’),true);
req.setRequestHeader(‘X-Requested-With’,’XMLHttpRequest’);// 設(shè)置請(qǐng)求頭信息
req.send(null); //發(fā)送一個(gè)請(qǐng)求

動(dòng)態(tài)腳本注入
這種技術(shù)克服了XHR的最大限制,它能跨域請(qǐng)求數(shù)據(jù)。但是這種方法也有自己的限制,那就是提供的控制是有限的,

不能設(shè)置請(qǐng)求的頭信息;

只能使用GET,不能POST;

不能設(shè)置請(qǐng)求的超時(shí)處理和重試;

不能訪問請(qǐng)求的頭信息。
這種請(qǐng)求的請(qǐng)求結(jié)果必須是可執(zhí)行的JavaScript代碼。所以無論傳輸來的什么數(shù)據(jù),都必須封裝在一個(gè)回調(diào)函數(shù)中。盡管限制諸多,但是這項(xiàng)技術(shù)的速度非常快。動(dòng)態(tài)腳本注入的使用方法如下:

var scriptElement = document.createElement("script");
scriptElement.src = "http://.../lib.js";
document.getElementsByTagName("head")[0].appendChild(scriptElement);

function jsonCallback(jsonString){
    var data = eval("("+jsonString+")")
}

jsonCallback({"state":1,"colors":["#fff","#000","#ff0000"]});

multipart XHR
這種方法允許客戶端只用一個(gè)HTTP請(qǐng)求就從服務(wù)器端向客戶端傳送多個(gè)資源,他通過在服務(wù)器端將資源(CSS文件,HTML片段,JavaScript代碼或base64編碼的圖片)打包為一個(gè)由雙方約定的字符串分割的長(zhǎng)字符串并發(fā)送給客戶端;然后用JavaScript代碼處理這個(gè)長(zhǎng)字符串,并根據(jù)它的mime-type類型和傳入的其它“頭信息”解析出每個(gè)資源。

基于這種方法,我們可以通過監(jiān)聽readyState為3的狀態(tài)來實(shí)現(xiàn)在每個(gè)資源收到時(shí)就立即處理,而不是等待整個(gè)響應(yīng)消息完成。

此技術(shù)最大的缺點(diǎn)是以這種方式獲得的資源不能被瀏覽器緩存。不過在某些情況下MXHR依然能顯著提升頁(yè)面的整體性能:

- 頁(yè)面中包含了大量其它地方用不到的資源比如圖片;
* 網(wǎng)站在每個(gè)頁(yè)面中使用一個(gè)獨(dú)立打包的JavaScript或CSS文件用以減少HTTP請(qǐng)求;

HTTP請(qǐng)求是Ajax中最大的瓶頸之一,因此減少HTTP請(qǐng)求的數(shù)量也許明顯的提升整個(gè)頁(yè)面的性能。

數(shù)據(jù)發(fā)送方式
主要有兩種數(shù)據(jù)發(fā)送方法

-     `XHR`
- 信標(biāo)`beacons`

XHR我們較熟悉,使用示例如下:

var url = ‘/data/php’;
var parms = [
    ‘id=934345’,
    ‘limit=20’
]

var req = new XMLHttpRequest();

req.onerror = function(){
    //出錯(cuò)
};

req.onreadystatechange = function(){
    if(req.readyState==4){
        // 成功
    }
}

req.open(‘POST’,url,true);
req.setRequestHeader(‘Content-Type’,’application/x-www-form-urlencoded’);
req.setRequestHeader(‘Content-Length’,params.length);
req.send(params.join(‘&’));

需要注意的是,使用XHR發(fā)送數(shù)據(jù)時(shí),GET方式更快,對(duì)少量數(shù)據(jù)而言,一個(gè)GRT請(qǐng)求往服務(wù)器只發(fā)送一個(gè)數(shù)據(jù)包。而一個(gè)POST請(qǐng)求至少發(fā)送兩個(gè)數(shù)據(jù)包,一個(gè)裝載頭信息,另外一個(gè)裝載POST正文,POST適合發(fā)送大量數(shù)據(jù)到服務(wù)器的情況。

Beacons技術(shù)類似于動(dòng)態(tài)腳本注入。
Beacons技術(shù)具體做法為使用JavaScript創(chuàng)建一個(gè)新的Image對(duì)象,并把src屬性設(shè)置為服務(wù)器上腳本的URL,該URL包含了我們要通過GET傳回的鍵值對(duì)數(shù)據(jù)。實(shí)際上并沒有創(chuàng)建img元素或把它傳入DOM。

var url = ‘/status_tracker.php’;
var params = [
    ‘step=2’,
    ‘time=1248027314’
];

(new Image()).src = url + ‘?’ + params.join(‘&’);

beacon.onload = function(){
    if(this.width==1){
        // 成功
    }else if(this.width==2){
        // 失敗,請(qǐng)重試并創(chuàng)建另一個(gè)信標(biāo)
    }
};

beacon.onerror = function(){
    // 出錯(cuò),稍后重試并創(chuàng)建另一個(gè)信標(biāo)
}

這種方法最大的優(yōu)勢(shì)是可以發(fā)送的數(shù)據(jù)的長(zhǎng)度被限制得非常小。Beacons技術(shù)是向服務(wù)器回傳數(shù)據(jù)最快且最有效的方式。唯一的缺點(diǎn)是能接收到的響應(yīng)類似是有限的。

數(shù)據(jù)格式

說完傳輸方式,我們?cè)儆懻撘幌聜鬏數(shù)臄?shù)據(jù)格式:
考慮數(shù)據(jù)格式時(shí),唯一需要比較的標(biāo)準(zhǔn)是速度
沒有那種數(shù)據(jù)格式會(huì)始終比其它格式更好。優(yōu)劣取決于要傳輸?shù)臄?shù)據(jù)以及它在頁(yè)面上的用途,有的數(shù)據(jù)格式可能下載速度更快,有的數(shù)據(jù)格式可能解析更快。常見的數(shù)據(jù)格式有以下四種:

XML
優(yōu)勢(shì):極佳的通用性(服務(wù)端和客戶端都能完美支持),格式嚴(yán)格,易于驗(yàn)證;

缺點(diǎn):極其冗長(zhǎng),每個(gè)多帶帶的數(shù)據(jù)片段都依賴大量結(jié)構(gòu),有效數(shù)據(jù)比例非常低,XML語法有些模糊,解析XML要占用JavaScript程序員相當(dāng)部分的精力。

JSON
優(yōu)勢(shì):體積更小,在響應(yīng)信息中結(jié)構(gòu)所占的比例小,JSON有著極好的通用性,大多數(shù)服務(wù)器端編程語言都提供了編碼和解碼的類庫(kù)。對(duì)于web開發(fā)而言,它是性能表現(xiàn)最好的數(shù)據(jù)格式;

使用注意:

- 使用`eval()`或盡量使用JSON.parse()方法解析字符串本身。
- 數(shù)組JSON的解析速度最快,但是可識(shí)別性最低。
- 使用XHR時(shí),JSON數(shù)據(jù)被當(dāng)做字符串返回,該字符串緊接著被eval()裝換為原生對(duì)象;
- JSON-P(JSON with padding):JSON-P因?yàn)榛卣{(diào)包裝的原因略微增大了文件尺寸,但是其被當(dāng)做原生js處理,解析速度快了10倍。

HTML
優(yōu)勢(shì):獲取后可以直接插入到DOM中,適用于前端CPU是瓶頸而帶寬非瓶頸的情況;

缺點(diǎn):作為數(shù)據(jù)結(jié)構(gòu),它即緩慢又臃腫。

自定義格式
一般我們采用字符分隔的形式。并用split()對(duì)字符串進(jìn)行分割,split()對(duì)字符串操作其實(shí)也是非常快的,通常它能在數(shù)毫秒內(nèi)處理包含10000+個(gè)元素的‘分隔符分隔’列表。

對(duì)數(shù)據(jù)格式的總結(jié)

- 使用JSON-P數(shù)據(jù),通過動(dòng)態(tài)腳本注入使用這種數(shù)據(jù),這種方法把數(shù)據(jù)當(dāng)做可執(zhí)行JavaScript而不是字符串,解析速度極快,能跨域使用,但是設(shè)計(jì)敏感數(shù)據(jù)時(shí)不應(yīng)該使用它;
* 我們也可以采用通過字符分隔的自定義格式,可以通過使用XHR或動(dòng)態(tài)腳本注入獲取對(duì)于數(shù)據(jù),并用`split()`解析。這項(xiàng)技術(shù)解析速度比JSON-P略快,通常文件尺寸更小。
針對(duì)Ajax的數(shù)據(jù)緩存
- 在服務(wù)器端,設(shè)置HTTP頭信息以確保響應(yīng)會(huì)被瀏覽器緩存;
- 設(shè)置頭信息后,瀏覽器只會(huì)在文件不存在緩存時(shí)才會(huì)向服務(wù)器發(fā)送Ajax請(qǐng)求;
* 在客戶端,將獲取到的信息存儲(chǔ)到本地,從而避免再次請(qǐng)求;

總的來說,對(duì)XHR創(chuàng)造性的使用是一個(gè)反應(yīng)遲鈍且平淡無奇的頁(yè)面與響應(yīng)快速且高效的頁(yè)面的區(qū)別所在,是一個(gè)用戶痛恨的站點(diǎn)與用戶迷戀的站點(diǎn)的區(qū)別所在,合理使用Ajax意義非凡。

合理構(gòu)建和部署JavaScript應(yīng)用

上文所述的都是在編碼過程中應(yīng)該注意的一些事項(xiàng),除此之外,在代碼上線時(shí),我們也可以做一些相關(guān)的優(yōu)化。

1. 合并多個(gè)JavaScript文件用以減少頁(yè)面渲染所需的HTTP請(qǐng)求數(shù);
2. JavaScript壓縮:通過剝離注釋和不必要的空白字符等,可以將文件大小減半,有多種工具可以完成壓縮:比如在線的YUI Compressor,在webpack中使用UglifyJsPlugin插件等;徹底的壓縮常做以下事情:
    * 將局部變量變?yōu)楦痰男问剑?    * 盡可能用雙括號(hào)表示法替換點(diǎn)表示法;
    * 盡可能去掉直接量屬性名的引號(hào);
    * 替換字符串的中的轉(zhuǎn)義字符;’aaabb’被替換為”aaa’bbb”
    * 合并常量;
3. 除了常規(guī)的JavaScript壓縮,我們還可以對(duì)代碼進(jìn)行Gzip壓縮,Gzip是目前最流行的編碼方式,它通常能減少70%的下載量,其主要適用于文本包括JS文件,其它類型諸如圖片或PDF文件,不應(yīng)該使用Gzip;
4. 基于不同的瀏覽器環(huán)境,我們應(yīng)該選擇發(fā)送最合適的代碼,比如說目前iPhone版的微信內(nèi)置瀏覽器是支持解壓Gzip的而安卓端默認(rèn)不支持,那對(duì)iPhone端就可以發(fā)送xxx.js,gz文件而對(duì)安卓端發(fā)送xxx.js文件,這樣是可以提高iPhone端的webApp的加載效率的;
5. 合并,預(yù)處理,壓縮等都既可以在構(gòu)建時(shí)完成,也可以在項(xiàng)目運(yùn)行時(shí)完成,但是推薦能在構(gòu)建時(shí)完成的就盡量在構(gòu)建時(shí)完成;
6. 合理緩存JavaScript文件也能提高之后打開相同網(wǎng)頁(yè)的效率:
    - Web服務(wù)器通過“Expires HTTP響應(yīng)頭”來告訴客戶端一個(gè)資源應(yīng)當(dāng)緩存多長(zhǎng)時(shí)間;
    * 移動(dòng)端瀏覽器大多有緩存限制(iPhone Safari 25k),這種情況下應(yīng)該權(quán)衡HTTP組件數(shù)量和它們的可緩存性,考慮將它們分為更小的塊;
    * 合理利用HTML5 離線應(yīng)用緩存
    - 應(yīng)用更新時(shí)有緩存的網(wǎng)頁(yè)可能會(huì)來不及更新(這種情況可以通過更新版本號(hào)或開發(fā)編號(hào)來解決);
7. 使用內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN);
 CDN是在互聯(lián)網(wǎng)上按地理位置分布的計(jì)算機(jī)網(wǎng)絡(luò),它負(fù)責(zé)傳遞內(nèi)容給終端用戶。使用CDN的主要原因是增強(qiáng)Web應(yīng)用的可靠性,可拓展性,更重要的是提升性能;
8. 值得注意的是,上述很多操作是可以通過自動(dòng)化處理完成的,學(xué)習(xí)相關(guān)自動(dòng)化處理工具可以大大提高我們的開發(fā)效率
總結(jié)

本文從多方面敘述了web前端的優(yōu)化思路,謝謝你讀到這里,希望你有所收獲,很多知識(shí)通過多次刻意的重復(fù)就能成為自己的潛意識(shí),希望我們?cè)诮窈蠖寄茉谧约旱膶?shí)際開發(fā)過程中,都能以效率更高的方式寫JS語句,操作DOM,我們的應(yīng)用都非常流暢,UI都不會(huì)阻塞,如果你有別的關(guān)于優(yōu)化的具體建議,歡迎一起討論。

后記

本文其實(shí)算是我讀Nicbolas C.Zakas的《高性能JavaScript》的讀書筆記,針對(duì)某個(gè)話題系統(tǒng)的讀書對(duì)我來說,是非常有好處的。系統(tǒng)的讀前端方面,計(jì)算機(jī)方面的經(jīng)典書籍也是我給自己安排的2017年最主要的任務(wù)之一,預(yù)計(jì)每月針對(duì)某本書或某幾本書關(guān)于某一個(gè)方面,寫一篇讀書筆記,本文是2017年的第一篇。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81256.html

相關(guān)文章

  • 練習(xí)項(xiàng)目備選清單

    摘要:練習(xí)項(xiàng)目備選清單文件下載器功能概要設(shè)計(jì)實(shí)現(xiàn)新建下載功能以為基礎(chǔ)給出下載鏈接可以啟動(dòng)下載任務(wù)實(shí)現(xiàn)局域網(wǎng)內(nèi)下載傳輸文件以單線程下載方式實(shí)現(xiàn)附加功能支持?jǐn)帱c(diǎn)續(xù)傳實(shí)現(xiàn)多線程下載實(shí)現(xiàn)下載參考技術(shù)套接字編程多線程編程音視頻播放器功能概要設(shè)計(jì)實(shí)現(xiàn)播放常見 練習(xí)項(xiàng)目備選清單 Utilities 1. 文件下載器 功能概要設(shè)計(jì): 實(shí)現(xiàn)新建下載功能(以ftp為基礎(chǔ)) 給出下載鏈接可以啟動(dòng)下載任務(wù) 實(shí)現(xiàn)局...

    guyan0319 評(píng)論0 收藏0
  • 練習(xí)項(xiàng)目備選清單

    摘要:練習(xí)項(xiàng)目備選清單文件下載器功能概要設(shè)計(jì)實(shí)現(xiàn)新建下載功能以為基礎(chǔ)給出下載鏈接可以啟動(dòng)下載任務(wù)實(shí)現(xiàn)局域網(wǎng)內(nèi)下載傳輸文件以單線程下載方式實(shí)現(xiàn)附加功能支持?jǐn)帱c(diǎn)續(xù)傳實(shí)現(xiàn)多線程下載實(shí)現(xiàn)下載參考技術(shù)套接字編程多線程編程音視頻播放器功能概要設(shè)計(jì)實(shí)現(xiàn)播放常見 練習(xí)項(xiàng)目備選清單 Utilities 1. 文件下載器 功能概要設(shè)計(jì): 實(shí)現(xiàn)新建下載功能(以ftp為基礎(chǔ)) 給出下載鏈接可以啟動(dòng)下載任務(wù) 實(shí)現(xiàn)局...

    peixn 評(píng)論0 收藏0
  • 前端修煉の道 | 如何成為一名合格前端開發(fā)工程師?

    摘要:上期回顧在上一節(jié)我們已了解前端開發(fā)是做什么的,現(xiàn)在的問題是,如何才能成為一名合格的前端開發(fā)工程師相信這個(gè)問題是大家比較關(guān)心的。 showImg(https://segmentfault.com/img/bVbi9ks?w=900&h=383);上期回顧 在上一節(jié)我們已了解前端開發(fā)是做什么的,現(xiàn)在的問題是,如何才能成為一名合格的前端開發(fā)工程師? 相信這個(gè)問題是大家比較關(guān)心的。 前端開發(fā)工...

    Jackwoo 評(píng)論0 收藏0
  • 使用Chrome DevToolsTimeline分析頁(yè)面性能

    摘要:如果網(wǎng)頁(yè)動(dòng)畫能夠做到每秒幀,就會(huì)跟顯示器同步刷新,達(dá)到最佳的視覺效果。下面的一條是,低于這條線,可以達(dá)到每秒幀上面的一條是,低于這條線,可以達(dá)到每秒次渲染。圖中幀柱的高度表示了該幀的總耗時(shí),幀柱中的顏色分別對(duì)應(yīng)該幀中包含的不停類型的事件。 原文地址:http://horve.github.io/2015/10/26/timeli... 隨著webpage可以承載的表現(xiàn)形式更加多樣化,通...

    v1 評(píng)論0 收藏0
  • 前端每周清單第 29 期:Web 現(xiàn)狀分析與優(yōu)化策略、Vue 單元測(cè)試、Headless Chrom

    摘要:前端每周清單第期現(xiàn)狀分析與優(yōu)化策略單元測(cè)試爬蟲作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。 showImg(https://segmentfault.com/img/remote/1460000011008022); 前端每周清單第 29 期:Web 現(xiàn)狀分析與優(yōu)化策略...

    HackerShell 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<