高性能移動Web相較PC的場景須要考慮的因素也相對更多更復雜,咱們總結爲如下幾點: 流量、功耗與流暢度。 在PC時代咱們更多的是考慮體驗上的流暢度,而在Mobile端自己豐富的場景下,須要額外關注對用戶基站網絡流量使用的狀況,設備耗電量的狀況。javascript
關於流暢度,主要體如今前端動畫中,在現有的前端動畫體系中,一般有兩種模式:JS動畫與CSS3動畫。 JS動畫是經過JS動態改寫樣式實現動畫能力的一種方案,在PC端兼容低端瀏覽器中不失爲一種推薦方案。 而在移動端,咱們選擇性能更優瀏覽器原生實現方案:CSS3動畫。css
然而,CSS3動畫在移動多終端設備場景下,相比PC會面對更多的性能問題,主要體如今動畫的卡頓與閃爍。前端
目前對提高移動端CSS3動畫體驗的主要方法有幾點:java
-webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); -ms-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0);
如動畫過程有閃爍(一般發生在動畫開始的時候),能夠嘗試下面的Hack:css3
-webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden; -webkit-perspective: 1000; -moz-perspective: 1000; -ms-perspective: 1000; perspective: 1000;
以下面一個元素經過translate3d
右移500px
的動畫流暢度會明顯優於使用left
屬性:git
#ball-1 { transition: -webkit-transform .5s ease; -webkit-transform: translate3d(0, 0, 0); } #ball-1.slidein { -webkit-transform: translate3d(500px, 0, 0); } #ball-2 { transition: left .5s ease; left: 0; } #ball-2.slidein { left: 500px; }
注:3D變形會消耗更多的內存與功耗,應確實有性能問題時纔去使用它,兼在權衡github
box-shadows
與gradients
box-shadows
與gradients
每每都是頁面的性能殺手,尤爲是在一個元素同時都使用了它們,因此擁抱扁平化設計吧。web
position: fixed; position: absolute;
咱們從實例開始描述這個主題:瀏覽器
var newWidth = aDiv.offsetWidth + 10; aDiv.style.width = newWidth + 'px'; var newHeight = aDiv.offsetHeight + 10; aDiv.style.height = newHeight + 'px'; var newWidth = aDiv.offsetWidth + 10; var newHeight = aDiv.offsetHeight + 10; aDiv.style.width = newWidth + 'px'; aDiv.style.height = newHeight + 'px';
這是兩段能力上徹底等同的代碼,顯式的差別正如咱們所見,只有執行順序的區別。但真是如此嗎?下面是加了說明註釋的代碼版本,很好的闡述了其中的進一步差別:網絡
// 觸發兩次 layout var newWidth = aDiv.offsetWidth + 10; // Read aDiv.style.width = newWidth + 'px'; // Write var newHeight = aDiv.offsetHeight + 10; // Read aDiv.style.height = newHeight + 'px'; // Write // 只觸發一次 layout var newWidth = aDiv.offsetWidth + 10; // Read var newHeight = aDiv.offsetHeight + 10; // Read aDiv.style.width = newWidth + 'px'; // Write aDiv.style.height = newHeight + 'px'; // Write
從註釋中可找到規律,連續的讀取offsetWidth/Height屬性與連續的設置width/height屬性,相比分別讀取設置單個屬性可少觸發一次layout。
從結論看彷佛與執行隊列有關,沒錯,這是瀏覽器的優化策略。全部可觸發layout的操做都會被暫時放入 layout-queue
中,等到必須更新的時候,再計算整個隊列中全部操做影響的結果,如此就可只進行一次的layout,從而提高性能。
關鍵一,可觸發layout
的操做,哪些操做下會layout的更新(也稱爲reflow
或者relayout
)?
咱們從瀏覽器的源碼實現入手,以開源Webkit/Blink爲例, 對layout的更新,Webkit 主要經過 Document::updateLayout 與 Document::updateLayoutIgnorePendingStylesheets 兩個方法:
void Document::updateLayout() { ASSERT(isMainThread()); FrameView* frameView = view(); if (frameView && frameView->isInLayout()) { ASSERT_NOT_REACHED(); return; } if (Element* oe = ownerElement()) oe->document()->updateLayout(); updateStyleIfNeeded(); StackStats::LayoutCheckPoint layoutCheckPoint; if (frameView && renderer() && (frameView->layoutPending() || renderer()->needsLayout())) frameView->layout(); if (m_focusedNode && !m_didPostCheckFocusedNodeTask) { postTask(CheckFocusedNodeTask::create()); m_didPostCheckFocusedNodeTask = true; } } void Document::updateLayoutIgnorePendingStylesheets() { bool oldIgnore = m_ignorePendingStylesheets; if (!haveStylesheetsLoaded()) { m_ignorePendingStylesheets = true; HTMLElement* bodyElement = body(); if (bodyElement && !bodyElement->renderer() && m_pendingSheetLayout == NoLayoutWithPendingSheets) { m_pendingSheetLayout = DidLayoutWithPendingSheets; styleResolverChanged(RecalcStyleImmediately); } else if (m_hasNodesWithPlaceholderStyle) recalcStyle(Force); } updateLayout(); m_ignorePendingStylesheets = oldIgnore; }
從 updateLayoutIgnorePendingStylesheets
方法的內部實現可知,其也是對 updateLayout
方法的擴展,而且在現有的 layout 更新模式中,大部分場景都是調用 updateLayoutIgnorePendingStylesheets 來進行layout的更新。
搜索 Webkit 實現中調用 updateLayoutIgnorePendingStylesheets 方法的代碼, 獲得如下可致使觸發 layout 的操做:
Element
: clientHeight, clientLeft, clientTop, clientWidth, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, offsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth
Frame, HTMLImageElement
: height, width
Range
: getBoundingClientRect(), getClientRects()
SVGLocatable
: computeCTM(), getBBox()
SVGTextContent
: getCharNumAtPosition(), getComputedTextLength(), getEndPositionOfChar(), getExtentOfChar(), getNumberOfChars(), getRotationOfChar(), getStartPositionOfChar(), getSubStringLength(), selectSubString()
SVGUse
: instanceRoot
window
: getComputedStyle(), scrollBy(), scrollTo(), scrollX, scrollY, webkitConvertPointFromNodeToPage(), webkitConvertPointFromPageToNode()