自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

高性能滾動(dòng)scroll及頁(yè)面渲染優(yōu)化

開(kāi)發(fā) 前端
本文主要想談?wù)勴?yè)面優(yōu)化之滾動(dòng)優(yōu)化。主要內(nèi)容包括了為何需要優(yōu)化滾動(dòng)事件,滾動(dòng)與頁(yè)面渲染的關(guān)系,節(jié)流與防抖,pointer-events:none 優(yōu)化滾動(dòng)。

[[189530]]

最近在研究頁(yè)面渲染及web動(dòng)畫(huà)的性能問(wèn)題,以及拜讀《CSS SECRET》(CSS揭秘)這本大作。

本文主要想談?wù)勴?yè)面優(yōu)化之滾動(dòng)優(yōu)化。

主要內(nèi)容包括了為何需要優(yōu)化滾動(dòng)事件,滾動(dòng)與頁(yè)面渲染的關(guān)系,節(jié)流與防抖,pointer-events:none 優(yōu)化滾動(dòng)。因?yàn)楸疚纳婕傲撕芏嗪芏嗷A(chǔ),可以對(duì)照上面的知識(shí)點(diǎn),選擇性跳到相應(yīng)地方閱讀。

滾動(dòng)優(yōu)化的由來(lái)

滾動(dòng)優(yōu)化其實(shí)也不僅僅指滾動(dòng)(scroll 事件),還包括了例如 resize 這類(lèi)會(huì)頻繁觸發(fā)的事件。簡(jiǎn)單的看看:

  1. var i = 0; 
  2.  
  3. window.addEventListener('scroll',function(){ 
  4.  
  5.     console.log(i++); 
  6.  
  7. },false);  

輸出如下:   

 

在綁定 scroll 、resize 這類(lèi)事件時(shí),當(dāng)它發(fā)生時(shí),它被觸發(fā)的頻次非常高,間隔很近。如果事件中涉及到大量的位置計(jì)算、DOM 操作、元素重繪等工作且這些工作無(wú)法在下一個(gè) scroll 事件觸發(fā)前完成,就會(huì)造成瀏覽器掉幀。加之用戶(hù)鼠標(biāo)滾動(dòng)往往是連續(xù)的,就會(huì)持續(xù)觸發(fā) scroll 事件導(dǎo)致掉幀擴(kuò)大、瀏覽器 CPU 使用率增加、用戶(hù)體驗(yàn)受到影響。

在滾動(dòng)事件中綁定回調(diào)應(yīng)用場(chǎng)景也非常多,在圖片的懶加載、下滑自動(dòng)加載數(shù)據(jù)、側(cè)邊浮動(dòng)導(dǎo)航欄等中有著廣泛的應(yīng)用。

當(dāng)用戶(hù)瀏覽網(wǎng)頁(yè)時(shí),擁有平滑滾動(dòng)經(jīng)常是被忽視但卻是用戶(hù)體驗(yàn)中至關(guān)重要的部分。當(dāng)滾動(dòng)表現(xiàn)正常時(shí),用戶(hù)就會(huì)感覺(jué)應(yīng)用十分流暢,令人愉悅,反之,笨重不自然卡頓的滾動(dòng),則會(huì)給用戶(hù)帶來(lái)極大不舒爽的感覺(jué)。

滾動(dòng)與頁(yè)面渲染的關(guān)系

為什么滾動(dòng)事件需要去優(yōu)化?因?yàn)樗绊懥诵阅堋D撬绊懥耸裁葱阅苣?額……這個(gè)就要從頁(yè)面性能問(wèn)題由什么決定說(shuō)起。

我覺(jué)得搞技術(shù)一定要追本溯源,不要看到別人一篇文章說(shuō)滾動(dòng)事件會(huì)導(dǎo)致卡頓并說(shuō)了一堆解決方案優(yōu)化技巧就如獲至寶奉為圭臬,我們需要的不是拿來(lái)主義而是批判主義,多去源頭看看。

從問(wèn)題出發(fā),一步一步尋找到最后,就很容易找到問(wèn)題的癥結(jié)所在,只有這樣得出的解決方法才容易記住。

說(shuō)教了一堆廢話,不喜歡的直接忽略哈,回到正題,要找到優(yōu)化的入口就要知道問(wèn)題出在哪里,對(duì)于頁(yè)面優(yōu)化而言,那么我們就要知道頁(yè)面的渲染原理:

瀏覽器渲染原理我在我上一篇文章里也要詳細(xì)的講到,不過(guò)更多的是從動(dòng)畫(huà)渲染的角度去講的:《【W(wǎng)eb動(dòng)畫(huà)】CSS3 3D 行星運(yùn)轉(zhuǎn) && 瀏覽器渲染原理》 。

想了想,還是再簡(jiǎn)單的描述下,我發(fā)現(xiàn)每次 review 這些知識(shí)點(diǎn)都有新的收獲,這次換一張圖,以 chrome 為例子,一個(gè) Web 頁(yè)面的展示,簡(jiǎn)單來(lái)說(shuō)可以認(rèn)為經(jīng)歷了以下下幾個(gè)步驟:

 

  • JavaScript:一般來(lái)說(shuō),我們會(huì)使用 JavaScript 來(lái)實(shí)現(xiàn)一些視覺(jué)變化的效果。比如做一個(gè)動(dòng)畫(huà)或者往頁(yè)面里添加一些 DOM 元素等。
  • Style:計(jì)算樣式,這個(gè)過(guò)程是根據(jù) CSS 選擇器,對(duì)每個(gè) DOM 元素匹配對(duì)應(yīng)的 CSS 樣式。這一步結(jié)束之后,就確定了每個(gè) DOM 元素上該應(yīng)用什么 CSS 樣式規(guī)則。
  • Layout:布局,上一步確定了每個(gè) DOM 元素的樣式規(guī)則,這一步就是具體計(jì)算每個(gè) DOM 元素最終在屏幕上顯示的大小和位置。web 頁(yè)面中元素的布局是相對(duì)的,因此一個(gè)元素的布局發(fā)生變化,會(huì)聯(lián)動(dòng)地引發(fā)其他元素的布局發(fā)生變化。比如, 元素的寬度的變化會(huì)影響其子元素的寬度,其子元素寬度的變化也會(huì)繼續(xù)對(duì)其孫子元素產(chǎn)生影響。因此對(duì)于瀏覽器來(lái)說(shuō),布局過(guò)程是經(jīng)常發(fā)生的。
  • Paint:繪制,本質(zhì)上就是填充像素的過(guò)程。包括繪制文字、顏色、圖像、邊框和陰影等,也就是一個(gè) DOM 元素所有的可視效果。一般來(lái)說(shuō),這個(gè)繪制過(guò)程是在多個(gè)層上完成的。
  • Composite:渲染層合并,由上一步可知,對(duì)頁(yè)面中 DOM 元素的繪制是在多個(gè)層上進(jìn)行的。在每個(gè)層上完成繪制過(guò)程之后,瀏覽器會(huì)將所有層按照合理的順序合并成一個(gè)圖層,然后顯示在屏幕上。對(duì)于有位置重疊的元素的頁(yè)面,這個(gè)過(guò)程尤其重要,因?yàn)橐坏﹫D層的合并順序出錯(cuò),將會(huì)導(dǎo)致元素顯示異常。

這里又涉及了層(GraphicsLayer)的概念,GraphicsLayer 層是作為紋理(texture)上傳給 GPU 的,現(xiàn)在經(jīng)常能看到說(shuō) GPU 硬件加速,就和所謂的層的概念密切相關(guān)。但是和本文的滾動(dòng)優(yōu)化相關(guān)性不大,有興趣深入了解的可以自行 google 更多。

簡(jiǎn)單來(lái)說(shuō),網(wǎng)頁(yè)生成的時(shí)候,至少會(huì)渲染(Layout+Paint)一次。用戶(hù)訪問(wèn)的過(guò)程中,還會(huì)不斷重新的重排(reflow)和重繪(repaint)。

其中,用戶(hù) scroll 和 resize 行為(即是滑動(dòng)頁(yè)面和改變窗口大小)會(huì)導(dǎo)致頁(yè)面不斷的重新渲染。

當(dāng)你滾動(dòng)頁(yè)面時(shí),瀏覽器可能會(huì)需要繪制這些層(有時(shí)也被稱(chēng)為合成層)里的一些像素。通過(guò)元素分組,當(dāng)某個(gè)層的內(nèi)容改變時(shí),我們只需要更新該層的結(jié)構(gòu),并僅僅重繪和柵格化渲染層結(jié)構(gòu)里變化的那一部分,而無(wú)需完全重繪。顯然,如果當(dāng)你滾動(dòng)時(shí),像視差網(wǎng)站(戳我看看)這樣有東西在移動(dòng)時(shí),有可能在多層導(dǎo)致大面積的內(nèi)容調(diào)整,這會(huì)導(dǎo)致大量的繪制工作。

防抖(Debouncing)和節(jié)流(Throttling)

scroll 事件本身會(huì)觸發(fā)頁(yè)面的重新渲染,同時(shí) scroll 事件的 handler 又會(huì)被高頻度的觸發(fā), 因此事件的 handler 內(nèi)部不應(yīng)該有復(fù)雜操作,例如 DOM 操作就不應(yīng)該放在事件處理中。

針對(duì)此類(lèi)高頻度觸發(fā)事件問(wèn)題(例如頁(yè)面 scroll ,屏幕 resize,監(jiān)聽(tīng)用戶(hù)輸入等),下面介紹兩種常用的解決方法,防抖和節(jié)流。

防抖(Debouncing)

防抖技術(shù)即是可以把多個(gè)順序地調(diào)用合并成一次,也就是在一定時(shí)間內(nèi),規(guī)定事件被觸發(fā)的次數(shù)。

通俗一點(diǎn)來(lái)說(shuō),看看下面這個(gè)簡(jiǎn)化的例子:

  1. // 簡(jiǎn)單的防抖動(dòng)函數(shù) 
  2.  
  3. function debounce(func, wait, immediate) { 
  4.  
  5.     // 定時(shí)器變量 
  6.  
  7.     var timeout; 
  8.  
  9.     return function() { 
  10.  
  11.         // 每次觸發(fā) scroll handler 時(shí)先清除定時(shí)器 
  12.  
  13.         clearTimeout(timeout); 
  14.  
  15.         // 指定 xx ms 后觸發(fā)真正想進(jìn)行的操作 handler 
  16.  
  17.         timeout = setTimeout(func, wait); 
  18.  
  19.     }; 
  20.  
  21. }; 
  22.  
  23.   
  24.  
  25. // 實(shí)際想綁定在 scroll 事件上的 handler 
  26.  
  27. function realFunc(){ 
  28.  
  29.     console.log("Success"); 
  30.  
  31.  
  32.   
  33.  
  34. // 采用了防抖動(dòng) 
  35.  
  36. window.addEventListener('scroll',debounce(realFunc,500)); 
  37.  
  38. // 沒(méi)采用防抖動(dòng) 
  39.  
  40. window.addEventListener('scroll',realFunc);  

上面簡(jiǎn)單的防抖的例子可以拿到瀏覽器下試一下,大概功能就是如果 500ms 內(nèi)沒(méi)有連續(xù)觸發(fā)兩次 scroll 事件,那么才會(huì)觸發(fā)我們真正想在 scroll 事件中觸發(fā)的函數(shù)。

上面的示例可以更好的封裝一下:

  1. // 防抖動(dòng)函數(shù) 
  2.  
  3. function debounce(func, wait, immediate) { 
  4.  
  5.     var timeout; 
  6.  
  7.     return function() { 
  8.  
  9.         var context = this, args = arguments; 
  10.  
  11.         var later = function() { 
  12.  
  13.             timeout = null
  14.  
  15.             if (!immediate) func.apply(context, args); 
  16.  
  17.         }; 
  18.  
  19.         var callNow = immediate & !timeout; 
  20.  
  21.         clearTimeout(timeout); 
  22.  
  23.         timeout = setTimeout(later, wait); 
  24.  
  25.         if (callNow) func.apply(context, args); 
  26.  
  27.     }; 
  28.  
  29. }; 
  30.  
  31.   
  32.  
  33. var myEfficientFn = debounce(function() { 
  34.  
  35.     // 滾動(dòng)中的真正的操作 
  36.  
  37. }, 250); 
  38.  
  39.   
  40.  
  41. // 綁定監(jiān)聽(tīng) 
  42.  
  43. window.addEventListener('resize', myEfficientFn);  

節(jié)流(Throttling)

防抖函數(shù)確實(shí)不錯(cuò),但是也存在問(wèn)題,譬如圖片的懶加載,我希望在下滑過(guò)程中圖片不斷的被加載出來(lái),而不是只有當(dāng)我停止下滑時(shí)候,圖片才被加載出來(lái)。又或者下滑時(shí)候的數(shù)據(jù)的 ajax 請(qǐng)求加載也是同理。

這個(gè)時(shí)候,我們希望即使頁(yè)面在不斷被滾動(dòng),但是滾動(dòng) handler 也可以以一定的頻率被觸發(fā)(譬如 250ms 觸發(fā)一次),這類(lèi)場(chǎng)景,就要用到另一種技巧,稱(chēng)為節(jié)流函數(shù)(throttling)。

節(jié)流函數(shù),只允許一個(gè)函數(shù)在 X 毫秒內(nèi)執(zhí)行一次。

與防抖相比,節(jié)流函數(shù)最主要的不同在于它保證在 X 毫秒內(nèi)至少執(zhí)行一次我們希望觸發(fā)的事件 handler。

與防抖相比,節(jié)流函數(shù)多了一個(gè) mustRun 屬性,代表 mustRun 毫秒內(nèi),必然會(huì)觸發(fā)一次 handler ,同樣是利用定時(shí)器,看看簡(jiǎn)單的示例:

  1. // 簡(jiǎn)單的節(jié)流函數(shù) 
  2.  
  3. function throttle(func, wait, mustRun) { 
  4.  
  5.     var timeout, 
  6.  
  7.         startTime = new Date(); 
  8.  
  9.  
  10.     return function() { 
  11.  
  12.         var context = this, 
  13.  
  14.             args = arguments, 
  15.  
  16.             curTime = new Date(); 
  17.  
  18.  
  19.         clearTimeout(timeout); 
  20.  
  21.         // 如果達(dá)到了規(guī)定的觸發(fā)時(shí)間間隔,觸發(fā) handler 
  22.  
  23.         if(curTime - startTime >= mustRun){ 
  24.  
  25.             func.apply(context,args); 
  26.  
  27.             startTime = curTime; 
  28.  
  29.         // 沒(méi)達(dá)到觸發(fā)間隔,重新設(shè)定定時(shí)器 
  30.  
  31.         }else
  32.  
  33.             timeout = setTimeout(func, wait); 
  34.  
  35.         } 
  36.  
  37.     }; 
  38.  
  39. }; 
  40.  
  41. // 實(shí)際想綁定在 scroll 事件上的 handler 
  42.  
  43. function realFunc(){ 
  44.  
  45.     console.log("Success"); 
  46.  
  47.  
  48. // 采用了節(jié)流函數(shù) 
  49.  
  50. window.addEventListener('scroll',throttle(realFunc,500,1000));  

上面簡(jiǎn)單的節(jié)流函數(shù)的例子可以拿到瀏覽器下試一下,大概功能就是如果在一段時(shí)間內(nèi) scroll 觸發(fā)的間隔一直短于 500ms ,那么能保證事件我們希望調(diào)用的 handler 至少在 1000ms 內(nèi)會(huì)觸發(fā)一次。

使用 rAF(requestAnimationFrame)觸發(fā)滾動(dòng)事件

上面介紹的抖動(dòng)與節(jié)流實(shí)現(xiàn)的方式都是借助了定時(shí)器 setTimeout ,但是如果頁(yè)面只需要兼容高版本瀏覽器或應(yīng)用在移動(dòng)端,又或者頁(yè)面需要追求高精度的效果,那么可以使用瀏覽器的原生方法 rAF(requestAnimationFrame)。

requestAnimationFrame

window.requestAnimationFrame() 這個(gè)方法是用來(lái)在頁(yè)面重繪之前,通知瀏覽器調(diào)用一個(gè)指定的函數(shù)。這個(gè)方法接受一個(gè)函數(shù)為參,該函數(shù)會(huì)在重繪前調(diào)用。

rAF 常用于 web 動(dòng)畫(huà)的制作,用于準(zhǔn)確控制頁(yè)面的幀刷新渲染,讓動(dòng)畫(huà)效果更加流暢,當(dāng)然它的作用不僅僅局限于動(dòng)畫(huà)制作,我們可以利用它的特性將它視為一個(gè)定時(shí)器。(當(dāng)然它不是定時(shí)器)

通常來(lái)說(shuō),rAF 被調(diào)用的頻率是每秒 60 次,也就是 1000/60 ,觸發(fā)頻率大概是 16.7ms 。(當(dāng)執(zhí)行復(fù)雜操作時(shí),當(dāng)它發(fā)現(xiàn)無(wú)法維持 60fps 的頻率時(shí),它會(huì)把頻率降低到 30fps 來(lái)保持幀數(shù)的穩(wěn)定。)

簡(jiǎn)單而言,使用 requestAnimationFrame 來(lái)觸發(fā)滾動(dòng)事件,相當(dāng)于上面的:

  1. throttle(func, xx, 1000/60) //xx 代表 xx ms內(nèi)不會(huì)重復(fù)觸發(fā)事件 handler 

簡(jiǎn)單的示例如下:

  1. var ticking = false; // rAF 觸發(fā)鎖 
  2.  
  3.  
  4. function onScroll(){ 
  5.  
  6.   if(!ticking) { 
  7.  
  8.     requestAnimationFrame(realFunc); 
  9.  
  10.     ticking = true
  11.  
  12.   } 
  13.  
  14.  
  15.  
  16. function realFunc(){ 
  17.  
  18.     // do something... 
  19.  
  20.     console.log("Success"); 
  21.  
  22.     ticking = false
  23.  
  24.  
  25. // 滾動(dòng)事件監(jiān)聽(tīng) 
  26.  
  27. window.addEventListener('scroll', onScroll, false);  

上面簡(jiǎn)單的使用 rAF 的例子可以拿到瀏覽器下試一下,大概功能就是在滾動(dòng)的過(guò)程中,保持以 16.7ms 的頻率觸發(fā)事件 handler。

使用 requestAnimationFrame 優(yōu)缺點(diǎn)并存,首先我們不得不考慮它的兼容問(wèn)題,其次因?yàn)樗荒軐?shí)現(xiàn)以 16.7ms 的頻率來(lái)觸發(fā),代表它的可調(diào)節(jié)性十分差。但是相比 throttle(func, xx, 16.7) ,用于更復(fù)雜的場(chǎng)景時(shí),rAF 可能效果更佳,性能更好。

總結(jié)一下

  • 防抖動(dòng):防抖技術(shù)即是可以把多個(gè)順序地調(diào)用合并成一次,也就是在一定時(shí)間內(nèi),規(guī)定事件被觸發(fā)的次數(shù)。
  • 節(jié)流函數(shù):只允許一個(gè)函數(shù)在 X 毫秒內(nèi)執(zhí)行一次,只有當(dāng)上一次函數(shù)執(zhí)行后過(guò)了你規(guī)定的時(shí)間間隔,才能進(jìn)行下一次該函數(shù)的調(diào)用。
  • rAF:16.7ms 觸發(fā)一次 handler,降低了可控性,但是提升了性能和精確度。

簡(jiǎn)化 scroll 內(nèi)的操作

上面介紹的方法都是如何去優(yōu)化 scroll 事件的觸發(fā),避免 scroll 事件過(guò)度消耗資源的。

但是從本質(zhì)上而言,我們應(yīng)該盡量去精簡(jiǎn) scroll 事件的 handler ,將一些變量的初始化、不依賴(lài)于滾動(dòng)位置變化的計(jì)算等都應(yīng)當(dāng)在 scroll 事件外提前就緒。

建議如下:

避免在scroll 事件中修改樣式屬性 / 將樣式操作從 scroll 事件中剝離

 

輸入事件處理函數(shù),比如 scroll / touch 事件的處理,都會(huì)在 requestAnimationFrame 之前被調(diào)用執(zhí)行。

因此,如果你在 scroll 事件的處理函數(shù)中做了修改樣式屬性的操作,那么這些操作會(huì)被瀏覽器暫存起來(lái)。然后在調(diào)用 requestAnimationFrame 的時(shí)候,如果你在一開(kāi)始做了讀取樣式屬性的操作,那么這將會(huì)導(dǎo)致觸發(fā)瀏覽器的強(qiáng)制同步布局。

滑動(dòng)過(guò)程中嘗試使用 pointer-events: none 禁止鼠標(biāo)事件

大部分人可能都不認(rèn)識(shí)這個(gè)屬性,嗯,那么它是干什么用的呢?

pointer-events 是一個(gè) CSS 屬性,可以有多個(gè)不同的值,屬性的一部分值僅僅與 SVG 有關(guān)聯(lián),這里我們只關(guān)注 pointer-events: none 的情況,大概的意思就是禁止鼠標(biāo)行為,應(yīng)用了該屬性后,譬如鼠標(biāo)點(diǎn)擊,hover 等功能都將失效,即是元素不會(huì)成為鼠標(biāo)事件的 target。

可以就近 F12 打開(kāi)開(kāi)發(fā)者工具面板,給 標(biāo)簽添加上 pointer-events: none 樣式,然后在頁(yè)面上感受下效果,發(fā)現(xiàn)所有鼠標(biāo)事件都被禁止了。

那么它有什么用呢?

pointer-events: none 可用來(lái)提高滾動(dòng)時(shí)的幀頻。的確,當(dāng)滾動(dòng)時(shí),鼠標(biāo)懸停在某些元素上,則觸發(fā)其上的 hover 效果,然而這些影響通常不被用戶(hù)注意,并多半導(dǎo)致滾動(dòng)出現(xiàn)問(wèn)題。對(duì) body 元素應(yīng)用 pointer-events: none ,禁用了包括 hover 在內(nèi)的鼠標(biāo)事件,從而提高滾動(dòng)性能。

  1. .disable-hover { 
  2.  
  3. pointer-events: none; 
  4.  
  5.  

大概的做法就是在頁(yè)面滾動(dòng)的時(shí)候, 給 添加上 .disable-hover 樣式,那么在滾動(dòng)停止之前, 所有鼠標(biāo)事件都將被禁止。當(dāng)滾動(dòng)結(jié)束之后,再移除該屬性。

可以查看這個(gè) demo (https://dl.dropboxusercontent.com/u/2272348/codez/expensivescroll/demo.html)頁(yè)面。

上面說(shuō) pointer-events: none 可用來(lái)提高滾動(dòng)時(shí)的幀頻 的這段話摘自 pointer-events-MDN ,還專(zhuān)門(mén)有文章講解過(guò)這個(gè)技術(shù):

使用pointer-events:none實(shí)現(xiàn)60fps滾動(dòng) 。

這就完了嗎?沒(méi)有,張?chǎng)涡裼幸黄獙?zhuān)門(mén)的文章,用來(lái)探討 pointer-events: none 是否真的能夠加速滾動(dòng)性能,并提出了自己的質(zhì)疑:

pointer-events:none提高頁(yè)面滾動(dòng)時(shí)候的繪制性能?

結(jié)論見(jiàn)仁見(jiàn)智,使用 pointer-events: none 的場(chǎng)合要依據(jù)業(yè)務(wù)本身來(lái)定奪,拒絕拿來(lái)主義,多去源頭看看,動(dòng)手實(shí)踐一番再做定奪。

其他參考文獻(xiàn)(都是好文章,值得一讀):

  • 實(shí)例解析防抖動(dòng)(Debouncing)和節(jié)流閥(Throttling)
  • 無(wú)線性能優(yōu)化:Composite
  • Javascript高性能動(dòng)畫(huà)與頁(yè)面渲染
  • Google Developers–渲染性能
  • Web高性能動(dòng)畫(huà) 

 【編輯推薦】

責(zé)任編輯:龐桂玉 來(lái)源: 前端大全
相關(guān)推薦

2020-05-27 09:41:10

前端性能邊緣計(jì)算

2019-03-01 11:03:22

Lustre高性能計(jì)算

2023-04-10 11:18:38

前端性能優(yōu)化

2015-09-16 13:54:30

Android性能優(yōu)化渲染

2021-08-13 09:06:52

Go高性能優(yōu)化

2023-11-18 19:46:07

GPU架構(gòu)

2022-12-12 09:01:13

2023-03-22 18:31:10

Android頁(yè)面優(yōu)化

2016-12-11 10:08:31

高性能組網(wǎng)能力

2009-01-05 10:00:11

JSP優(yōu)化Servlet性能優(yōu)化

2023-08-29 15:10:04

持續(xù)性能優(yōu)化開(kāi)發(fā)

2014-03-19 14:34:06

JQuery高性能

2018-03-30 18:17:10

MySQLLinux

2009-09-08 09:45:23

App Engine性

2022-06-06 22:36:55

渲染性能CSS

2018-01-19 14:39:53

瀏覽器頁(yè)面優(yōu)化

2023-11-01 11:59:13

2019-03-14 15:38:19

ReactJavascript前端

2009-06-29 18:22:43

TomcatJSP頁(yè)面

2019-05-21 09:40:47

Elasticsear高性能 API
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)