新聞中心
時過境遷,Web 應用比以往任何時候都更具交互性。搞定性能可以幫助你極大地改善終端用戶的體驗。閱讀以下的技巧并學以致用,看看哪些可以用來改善延遲,渲染時間以及整體性能吧!

成都創(chuàng)新互聯(lián)專注于企業(yè)全網(wǎng)營銷推廣、網(wǎng)站重做改版、蘭山網(wǎng)站定制設計、自適應品牌網(wǎng)站建設、H5開發(fā)、商城系統(tǒng)網(wǎng)站開發(fā)、集團公司官網(wǎng)建設、成都外貿(mào)網(wǎng)站建設、高端網(wǎng)站制作、響應式網(wǎng)頁設計等建站業(yè)務,價格優(yōu)惠性價比高,為蘭山等各大城市提供網(wǎng)站開發(fā)制作服務。
更快的 Web 應用
優(yōu)化 Web 應用是一項費勁的工作。Web 應用不僅處于客戶端和服務器端的兩部分組件當中,通常來說也是由多種多樣的技術棧構(gòu)建而成:數(shù)據(jù)庫,后端組件(一般也是搭建在不同技術架構(gòu)之上的),以及前端(HTML + JavaScript + CSS + 轉(zhuǎn)譯器)。運行時也是變化多端的:iOS,Android,Chrome,F(xiàn)irefox,Edge。如果你曾經(jīng)工作在一個不同的單一龐大的平臺之上,通常情況下性能優(yōu)化只針對于單一目標(甚至只是目標的單一版本而已),但是現(xiàn)在的話你就可能會意識到任務復雜度要遠超于此。這就對了。但這兒也有一些通用的優(yōu)化指南可以大大優(yōu)化一個應用。我們將會在接下來的章節(jié)中探討這些指南的內(nèi)容。一份 Bing 的研究表明,頁面加載時間每增加 10ms,網(wǎng)站的年收入就會減少 25 萬美元。 —— Rob Trace 和 David Walp,微軟高級程序經(jīng)理
過早優(yōu)化?
優(yōu)化最難的地方就是如何在開發(fā)生命周期中最適當?shù)臅r候去做優(yōu)化。Donald Knuth 有一句名言:「過早優(yōu)化乃萬惡之源」。這句話背后的原因非常簡單:因為一不小心就會浪費時間去優(yōu)化某個 1% 的地方,但是結(jié)果卻并不會對性能造成什么重大影響。與此同時,一些優(yōu)化還妨礙了可讀性或者是可維護性,甚至還會引入新的 Bug。換句話說,優(yōu)化不應當被認為是「意味著得到應用程序的***性能」,而是「探索優(yōu)化應用的正確的方式,并得到***的效益」。再換句話說,盲目的優(yōu)化可能會導致效率的丟失,而收益卻很小。在你應用以下技巧的時候請將此銘記在心。你***的朋友就是分析工具:找到你可以進行通過優(yōu)化獲得***程度改善的性能點,而不用損害應用開發(fā)的進程或者可維護性。
程序員們浪費了大量時間來思考,或者說是擔憂,他們的程序中非關鍵部分的運行速度。并且他們對于性能的這些嘗試,實際上卻對代碼的調(diào)試和維護有著非常消極的影響。我們應當忘記那些不重要的性能影響,在 97% 的時間里都可以這么說:過早優(yōu)化乃萬惡之源。當然我們也不應當在那關鍵的 3% 上放棄我們的機會?!?Donald Knuth
1. JavaScript 壓縮和模塊打包
JavaScript 應用是以源碼形式進行分發(fā)的,而源碼解析的效率是要比字節(jié)碼低的。對于一小段腳本來說,區(qū)別可以忽略不計。但是對于更大型的應用,腳本的大小會對應用啟動時間有著負面的影響。事實上,寄期望于使用 WebAssembly 而獲得***程度的改善,其中之一就是可以得到更快的啟動時間。
另一方面,模塊打包則用于將不同腳本打包在一起并放進同一文件。更少的 HTTP 請求和單個文件解析都可以減少加載時間。通常情況下,單獨一種工具就可以處理打包和壓縮。Webpack 就是其中之一。
示例代碼:
- function insert(i) {
- document.write("Sample " + i);
- }
- for(var i = 0; i < 30; ++i) {
- insert(i);
- }
結(jié)果如下:
- !function(r){function t(o){if(e[o])return e[o].exports;var n=e[o]={exports:{},id:o,loaded:!1};returnr[o].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var e={};return t.m=r,t.c=e,t.p="",t(0)}([function(r,t){function e(r){document.write("Sample "+r)}for(var o=0;30>o;++o)e(o)}]);
- //# sourceMappingURL=bundle.min.js.map
進一步打包
你也可以使用 Webpack 打包 CSS 文件以及合并圖片。這些特性都可以有助于改善啟動時間。研究一下 Webpack 文檔來做些測試吧!
2. 按需加載資源
資源(特別是圖片)的按需加載或者說惰性加載,可以有助于你的 Web 應用在整體上獲得更好的性能。對于使用大量圖片的頁面來說惰性加載有著顯著的三個好處:
- 減少向服務器發(fā)出的并發(fā)請求數(shù)量(這就使得頁面的其他部分獲得更快的加載時間)
- 減少瀏覽器的內(nèi)存使用率(更少的圖片,更少的內(nèi)存)
- 減少服務器端的負載
大體上的理念就是只在必要的時候才去加載圖片或資源(如視頻),比如在***次被顯示的時候,或者是在將要顯示的時候?qū)ζ溥M行加載。由于這種方式跟你建站的方式密切相關,惰性加載的解決方案通常需要借助其他庫的插件或者擴展來實現(xiàn)。舉個例子,react-lazy-load 就是一個用于處理 React 惰性加載圖片的插件:
- const MyComponent = () => (
- Scroll to load images.
![]()
- (...)
一個非常好的實踐范例就像 Goggle Images 的搜索工具一樣。點擊前面的鏈接并且滑動頁面滾動條就可以看到效果了。
3. 在使用 DOM 操作庫時用上 array-ids
如果你正在使用 React,Ember,Angular 或者其他 DOM 操作庫,使用 array-ids(或者 Angular 1.x 中的 track-by 特性)非常有助于實現(xiàn)高性能,對于動態(tài)網(wǎng)頁尤其如此。我們已經(jīng)在上一篇程序衡量標準的文章中看到這個特性的效果了: More Benchmarks: Virtual DOM vs Angular 1 & 2 vs Mithril.js vs cito.js vs The Rest (Updated and Improved!)。
此特性背后的主要概念就是盡可能多地重用已有的節(jié)點。Array ids 使得 DOM 操作引擎可以「知道」在什么時候某個節(jié)點可以被映射到數(shù)組當中的某個元素。沒有 array-ids 或者 track-by 的話,大部分庫都會進行重新排序而摧毀已有的節(jié)點并重新創(chuàng)建新的。這就非常損耗性能了。
4. 緩存
Caches 是用于存儲那些被頻繁存取的靜態(tài)數(shù)據(jù)的組件,便于隨后對于這個數(shù)據(jù)的請求可以更快地被響應,或者說請求方式更加高效。由于 Web 應用是由很多可拆卸的部件組合而成,緩存就可以存在于架構(gòu)中的很多部分。舉例來說,緩存可以被放在動態(tài)內(nèi)容服務器和客戶端之間,就可以避免公共請求以減少服務器的負載,與此同時改善響應時間。其他緩存可能被放置在代碼里,以優(yōu)化某些用于腳本存取的通用模式,還有些緩存可能被放置在數(shù)據(jù)庫或者是長運行進程之前。
簡而言之,在 Web 應用中使用緩存是一種改善響應時間和減少 CPU 使用的***方式。難點就在于搞清楚哪里才是在架構(gòu)中存放緩存的地方。再一次,答案就是性能分析:常見的瓶頸在哪里?數(shù)據(jù)或者結(jié)果可緩存嗎?他們都太容易失效嗎?這都是一些棘手的問題,需要從原理上來一點一點回答。
緩存的使用在 Web 環(huán)境中富有創(chuàng)造性。比如,basket.js 就是一個使用Local Storage 來緩存應用腳本的庫。所以你的 Web 應用在第二次運行腳本的時候就可以幾乎瞬間加載了。
如今一個廣受歡迎的緩存服務就是亞馬遜的 CloudFront。CloudFront 就跟通常的內(nèi)容分發(fā)網(wǎng)絡(CDN)用途一樣,可以被設置作為動態(tài)內(nèi)容的緩存。
5. 啟用 HTTP/2
越來越多的瀏覽器都開始支持 HTTP/2。這可能聽起來沒有必要,但是 HTTP/2 為同一服務器的并發(fā)連接問題帶來了很多好處。換句話說,如果有很多小型資源需要加載(如果你打***的話就沒有必要了),在延遲和性能方面 HTTP/2 秒殺 HTTP/1。試試 Akamai 的 HTTP/2 demo,可以在***的瀏覽器中看到區(qū)別。
6. 應用性能分析
性能分析是優(yōu)化任何應用程序時的重要一步。就像介紹中所提到的那樣,盲目嘗試優(yōu)化應用經(jīng)常會導致效率的浪費,微不足道的收益和更差的可維護性。執(zhí)行性能分析是識別你的應用問題所在的一個重要步驟。
對于 Web 應用來說,延遲時間是***的抱怨之一,所以你需要確保數(shù)據(jù)的加載和顯示都盡可能得快。Chrome 提供了非常棒的性能分析工具。特別是 Chrome Dev Tools 中的時間線和網(wǎng)絡視圖都對于定位延遲問題有著很大的幫助:
時間線視圖可以幫忙找到運行時間較長的操作。
網(wǎng)絡視圖可以幫助識別出額外的由緩慢請求導致的延遲或?qū)τ谀骋欢它c的串行訪問。
正確分析的話,內(nèi)存則是另一塊可能獲得收益的部分。如果你正在運行著一個擁有很多虛擬元素的頁面(龐大的動態(tài)表格)或者可交互式的元素(比如游戲),內(nèi)存優(yōu)化可以獲得更少的卡頓和更高的幀率。從我們最近的文章 4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them 中,對于如何使用 Chrome 的開發(fā)工具有著進一步的深度理解。
CPU 性能分析也可以在 Chrome Dev Tools 中找到??纯催@篇來自 Google 官方文檔中的文章 Profiling JavaScript Performance。
找到性能損耗的中心可以讓你有效率地達到優(yōu)化的目標。
對后端的性能分析會更加困難。通常情況下,確認一個耗費較多時間的請求可以讓你明確應該優(yōu)先分析哪一個服務。對于后端的分析工具來說,則取決于所構(gòu)建的技術棧。
一個關于算法的注意事項
在大多數(shù)情況下,選擇一個更優(yōu)的算法,比圍繞著小成本中心所實現(xiàn)的具體優(yōu)化策略能夠獲得更大的收益。在某種程度上,CPU 和內(nèi)存分析應該可以幫你找到大的性能瓶頸。當這些瓶頸跟編碼問題并不相關時,則是時候考慮考慮不同的算法了。
7. 使用負載均衡方案
我們在之前討論緩存的時候簡要提到了內(nèi)容分發(fā)網(wǎng)絡(CDNs)。把負載分配到不同的服務器(甚至于不同的地理區(qū)域)可以給你的用戶提供更好的延遲時間,但是這條路還很漫長,特別是在處理很多的并發(fā)連接的時候。
負載均衡就跟使用某個 round-robin(循環(huán))解決方案一樣簡單,可以基于一個 nginx 反向代理 ,或者基于一個成熟的分布式網(wǎng)絡,比如 Cloudflare 或者 Amazon CloudFront。
以上的圖來自于 Citrix。 為了使負載均衡真正有效,動態(tài)內(nèi)容和靜態(tài)內(nèi)容都應該被拆分成易于并發(fā)訪問的。換句話說,元素的串形訪問會削弱負載均衡器以***形式進行分流的能力。與此同時,對于資源的并發(fā)訪問可以改善啟動時間。
雖然負載均衡可能會很復雜。對最終一致性算法不友好的數(shù)據(jù)模型,或者緩存都會讓事情更加困難。幸運的是,大多數(shù)應用對于已簡化的數(shù)據(jù)集都只需要保證高層次的一致性即可。如果你的應用程序沒有這樣設計的話,就有必要重構(gòu)一下了。
8. 為了更快的啟動時間考慮一下同構(gòu) JavaScript
改善 Web 應用程序觀感的方式之一,就是減少啟動時間或者減少首頁渲染時間。這對于新興的單頁面應用尤為重要,其需要在客戶端執(zhí)行大量任務。在客戶端做更多事情通常就意味著,在***次渲染被執(zhí)行之前就需要下載更多的信息。同構(gòu) JavaScript 可以解決這個問題:自從 JavaScript 可以同時運行在客戶端和服務器端,這就讓在服務器端來執(zhí)行頁面的***渲染成為可能,先把已渲染的頁面發(fā)送出去然后再由客戶端的腳本接管。這限制了所使用的后端(必須使用支持該特性的 JavaScript 框架),但卻能獲得更好的用戶體驗。舉例來說,React 就很適合于做這個,就像以下代碼所示:
- var React = require('react/addons');
- var ReactApp = React.createFactory(require('../components/ReactApp').ReactApp);
- module.exports = function(app) {
- app.get('/', function(req, res){
- // React.renderToString takes your component
- // and generates the markup
- var reactHtml = React.renderToString(ReactApp({}));
- // Output html rendered by react
- // console.log(myAppHtml);
- res.render('index.ejs', {reactOutput: reactHtml});
- });
- };
Meteor.js 對于客戶端和服務器端的 JavaScript 混用有著非常棒的支持。
- if (Meteor.isClient) {
- Template.hello.greeting = function () {
- return "Welcome to myapp.";
- };
- Template.hello.events({
- 'click input': function () {
- // template data, if any, is available in 'this'
- if (typeof console !== 'undefined')
- console.log("You pressed the button");
- }
- });
- }
- if (Meteor.isServer) {
- Meteor.startup(function () {
- // code to run on server at startup
- });
- }
但是,為了支持服務器端渲染,需要像 meteor-ssr 這樣的插件。
謝謝 gabrielpoca 在評論中指出這一點。如果你有復雜的或者中等大小的應用需要支持同構(gòu)部署,試試這個,你可能會感到驚訝的。
9. 使用索引加速數(shù)據(jù)庫查詢
如果你需要解決數(shù)據(jù)庫查詢耗費大量時間的問題(分析你的應用看看是否是這種情況!),是時候找出加速數(shù)據(jù)庫的方法了。每個數(shù)據(jù)庫和數(shù)據(jù)模型都有自己的權衡。數(shù)據(jù)庫優(yōu)化在每一方面都是一個主題:數(shù)據(jù)模型,數(shù)據(jù)庫類型,具體實現(xiàn)方案,等等。提速可能不是那么的簡單。但是這兒有個建議,可能可以對某些數(shù)據(jù)庫有所幫助:索引。索引是一個過程,即數(shù)據(jù)庫所創(chuàng)建的快速訪問數(shù)據(jù)結(jié)構(gòu),從內(nèi)部映射到鍵(在關系數(shù)據(jù)庫中的列),可以提高檢索相關數(shù)據(jù)的速度。大多數(shù)現(xiàn)代數(shù)據(jù)庫都支持索引。索引并不是文檔型數(shù)據(jù)庫(比如 MongoDB)所獨有的,也包括關系型數(shù)據(jù)庫(比如PostgreSQL)。
為了使用索引來優(yōu)化你的查詢,你將需要研究一下應用程序的訪問模式:什么是最常見的查詢,在哪個鍵或列中執(zhí)行搜索,等等。
10. 使用更快的轉(zhuǎn)譯方案
JavaScript 軟件技術棧一如既往的復雜。而改善語言本身的需求則又增加了復雜度。不幸地是,JavaScript 作為目標平臺又會被用戶的運行時所限制。盡管很多改進已經(jīng)以 ECMAScript 2015(2016正在進行)的形式實現(xiàn)了,但是通常情況下,對客戶端代碼來說又不可能依賴于這個版本。這種趨勢促使了一系列的轉(zhuǎn)譯器:用于處理 ECMAScript 2015 代碼的工具和只使用 ECMAScript 5 結(jié)構(gòu)實現(xiàn)其中所缺失的特性。與此同時,模塊綁定和壓縮處理也已經(jīng)被集成到這個生產(chǎn)過程中,被稱為為發(fā)布而構(gòu)建的代碼版本。這些工具可以轉(zhuǎn)化代碼,并且能夠以有限的方式影響到最終代碼的性能。Google 開發(fā)者 Paul Irish 花了一些時間來尋找這些轉(zhuǎn)譯方案會如何影響性能和最終代碼的大小。盡管大多數(shù)情況下收益會很小,但也值得在正式采用某個工具棧之前看看這些數(shù)據(jù)。對于大型應用程序來說,這種區(qū)別可能會影響重大。
11. 避免或最小化 JavaScript 和 CSS 的使用而阻塞渲染
JavaScript 和 CSS 資源都會阻塞頁面的渲染。通過采取某些的規(guī)則,你可以保證你的腳本和 CSS 被盡可能快速地處理,以便于瀏覽器能夠顯示你的網(wǎng)站內(nèi)容。
在 CSS 的情況下這是非常重要的,所有的 CSS 規(guī)則都不能與特定媒體直接相關,規(guī)則只用于處理你準備在頁面上所顯示內(nèi)容的優(yōu)先級。這可以通過使用 CSS 媒體查詢來實現(xiàn)。媒體查詢告訴瀏覽器,哪些 CSS 樣式表應用在某個特定的顯示媒體上。舉個例子,用于打印的某些規(guī)則可以被賦予比用于屏幕顯示更低的優(yōu)先級。
媒體查詢可以被設置成 標簽屬性:
輪到 JavaScript 了,關鍵就在于遵循某些用于內(nèi)聯(lián) JavaScript 的規(guī)則(比如內(nèi)聯(lián)在 HTML 文件當中的代碼)。內(nèi)聯(lián) JavaScript 應該盡可能短,并將其放在不會阻塞頁面剩余部分解析的地方。換句話說,被放在 HTML 樹中間的內(nèi)聯(lián) JavaScript 將會在這個地方阻塞解析器,并強制其等待直到腳本被執(zhí)行完畢。如果在 HTML 文件中隨意放了一些大的代碼塊或者很多小的代碼塊,對于性能來說這會成為性能殺手。內(nèi)聯(lián)可以有效減少額外對于某些特定腳本的網(wǎng)絡請求。但是對于重復使用的腳本或者大的代碼塊來說,這個好處就可以忽略不計了。
防止 JavaScript 阻塞解析器和渲染器的一種方法就是將
12. 用于未來的一個建議:使用 service workers + 流
Jake Archibald 最近的一篇博文詳細描述了一種有趣的技術,可以用于加速渲染時間:將 service workers 和流結(jié)合起來。結(jié)果非常令人嘆服:
不幸的是這個技術所需要的 APIs 都還不穩(wěn)定,這也是為什么這是一種有趣的概念但現(xiàn)在還沒有真正被應用的原因。這個想法的主旨就是在網(wǎng)站和客戶端之間放置一個 service worker。這個 service worker 可以在獲取缺失信息的同時緩存某些數(shù)據(jù)(比如 header 和一些不會經(jīng)常改變的東西)。缺失的內(nèi)容就可以盡可能快速地流向被渲染的頁面。
13. 更新:圖片編碼優(yōu)化
我們的一個讀者指出了一個非常重要的遺漏:圖片編碼優(yōu)化。PNGs 和 JPGs 在 Web 發(fā)布時都會使用次優(yōu)的設置進行編碼。通過改變編碼器和它的設置,對于需要大量圖片的網(wǎng)站來說可以獲得有效的改善。流行的解決方案包括 OptiPNG 和jpegtran。
A guide to PNG optimization(http://optipng.sourceforge.net/pngtech/optipng.html) 詳細描述了 OptiPNG 可以如何用于優(yōu)化 PNGs。
The man page for jpegtran(http://linux.die.net/man/1/jpegtran) 對它的一些特性提供了很好的介紹。
如果你發(fā)現(xiàn)這些指南相對于你的要求來說都太復雜了的話,這兒有一些在線網(wǎng)站可以提供優(yōu)化服務。也有一些像 RIOT 一樣的圖形化界面,非常有助于批量操作和結(jié)果檢查。
悄悄話:Auth0 中常見的優(yōu)化
我們是一個 Web 公司。就以這種身份來說,我們?yōu)槲覀兊幕A設施的某些部分部署了一些特定的優(yōu)化。舉例來說,在登錄頁面你可以發(fā)現(xiàn),在我們域名的 /learn 路徑下(比如,登錄頁面的單點登錄),我們采用了一種特別的優(yōu)化:為了方便我們使用 CMS 來創(chuàng)建每篇文章。因為文章都沒有中心索引,但是為了能夠被搜索引擎發(fā)現(xiàn),使用了 webtask 的爬蟲來預渲染每個頁面并生成了一個靜態(tài)版本然后上傳到我們 CDN。這減少了我們在服務器端上的壓力,因為無須為每個訪客都生成動態(tài)的服務器端內(nèi)容。與此同時還改善了延遲(并且隔離了我們發(fā)現(xiàn)與 CMS 相關的安全問題)。
對于文檔部分,我們正在使用同構(gòu) JavaScript,這讓我們獲得了非常棒的啟動時間,并且使我們的后端和前端團隊能夠輕松集成。
結(jié)論
由于應用程序變得越來越大和越來越復雜,性能優(yōu)化對于 Web 開發(fā)來說正在變得越來越重要。在做出任何值得的時間和潛在的未來成本的優(yōu)化嘗試時,有針對性的改進都是必不可少的。Web 應用程序早已突破了大多數(shù)靜態(tài)內(nèi)容的邊界,學習常見模式進行優(yōu)化則是令人愉悅的應用和完全不可用的應用之間***的區(qū)別(這是讓你的訪客留下來的長遠之計?。?。沒有什么規(guī)則是絕對的,但是:性能分析和研究特定軟件技術棧的錯綜復雜之處,是找出如何優(yōu)化它的唯一方式。
當前標題:唯快不破:Web應用的13個優(yōu)化步驟
網(wǎng)頁URL:http://www.dlmjj.cn/article/dphosic.html


咨詢
建站咨詢
