給網頁設計師和前端開發者看的前端性能優化

英文原文:Front-end performance for web designers and front-end developers ,編譯:oschina

如果不是不可能,你也很難拒絕承認性能目前已是任何正規網站項目的最關鍵方面之一,無論它是一個小型的網站組合,一個移動優先的web應用,一直到一個大規模的商業項目。研究,論文和個人體驗都告訴我們快速是最好的。

性能不僅僅是非常重要,它也相當的有趣,而且這也是我越來越投入的事,不僅在工作方面(我一直在煩我們的首席性能工程師),也在項目與CSS魔法網站方面(我一直在煩Andy Davies)。

在這篇長文章中,我將分享收穫,關於快速,簡單且非常有趣的性能知識的點點滴滴,以便使你的行爲可以像一個初級的網頁設計師和前端開發者;希望對任何想開始學習性能的人,這篇文章可以成爲一個正規的介紹,並使它們的前端變得超快。這些技巧是你能非常容易實現的。它只需要一點小技巧,以及一些瀏覽器怎樣工作的基礎知識,你就能開始玩轉系統了!

這篇長帖子不會講到模糊圖像的加載和數據處理,取而代之的是有關理論與第一手的性能方面的技術資料,這些技術是我通過閱讀,觀察,蒐集和整理獲得的(我花費了許多時間沉浸於CSS魔法的瀑布圖)。我也會鏈接到其它相似話題的文章,以便幫助強化一些關鍵要點。享受吧!

注意 本文需要預先知曉一些基礎的性能知識,如果有任何你不熟悉的就Google搜索一下好了!

基礎知識

關於性能,有一些知識在所有的設計師和前端開發者中廣爲傳播。例如,儘可能少的請求,優化圖片,把樣式表(stylesheets)放在<head>, 把JS放在</body>之前, 最小化(minifying) JS 和 CSS 等等。這些基礎知識已經被用來加快用戶響應了,但還有更多更多需要學習。

雖然在我們每天的工作生活中,瀏覽器給我們製造麻煩,使我們頭疼,但請記住,他們也是很聰明的; 它們爲我們做了很多性能優化工作, 所以大量的性能調優知識不但要知道瀏覽器在哪裏給我們做了優化,還要知道怎麼更好的挖掘它們。大量性能調優訣竅只是理解,利用和操縱瀏覽器已經替我們做好的優化工作。

頂部的Styles, 底部的scripts

這真的是一條基本規則,每個人都能非常容易的在大多數時間遵守,但爲什麼它重要?簡短的說:

  • CSS 塊渲染, 因此你需要立即處理它(即在文檔的頂部,在你的<head>之中)。
  • JS 塊下載, 因此你需要最後處理它們,以確保它們沒有耽誤頁面中任何其它東西。

CSS塊渲染是因爲瀏覽器總是試圖漸進式的渲染頁面;它們想在元素到達的時候順序的渲染它。如果style在距離很遠的頁面下部,瀏覽器在獲得它之前沒有辦法渲染那個CSS。因爲這個原因,如果瀏覽器在渲染文檔過程中,改變了之前渲染的東西,它們可以避免style的重繪。瀏覽器在它獲得所有需要的style信息之前不會渲染頁面,如果你將style放在文檔底部,你就是在使瀏覽器等待,阻塞了渲染。

所以,只要你將CSS放在頁面的頂部,那麼瀏覽器就可以立刻開始渲染。

JavaScript塊下載是由於好幾個原因(這又是瀏覽器聰明之處),但首先我們需要知道瀏覽器裏的資源下載是如何實際發生的;簡單的說,瀏覽器會從一個單一的域名並行的儘可能多的下載資源。它從越多的域名下載,就能在一瞬間並行的獲得更多的資源。

JavaScript中斷了這個過程,阻塞了從任何一個域名的並行的下載,因爲:

  • 被調用的腳本可能改變頁面,即瀏覽器在繼續別的事情以前,將不得不處理它。因此爲了處理那個不測事件,瀏覽器停止了任何其它東西的下載,以便集中精力關注於它。
  • 腳本正常工作經常需要依照一定的順序加載,例如,要在加載一個插件之前加載jQuery。瀏覽器阻止了JavaScript的並行下載,因此它不會同時下載jQuery和你的插件;很顯然如果你同時並行下載二者,你的插件會在jQuery之前到達。

所以,由於瀏覽器在獲取JavaScript的時候停止了所有其他下載,將你的JavaScript腳本放在文檔中儘可能晚加載的地方是一個好主意。我相信你們都看到過頁面中的空白片段,在那裏第三方的JS腳本被花時間加載,並且它還阻止了頁面其他資源的獲取和渲染;這就是JavaScript的阻塞在作用了。

但是顯然,現代瀏覽器還是變得聰明瞭。我將給你一個Andy Davies寄給我的電子郵件的摘錄,因爲他解釋的比我清楚:

現代瀏覽器將並行下載JS,只有在腳本被執行的時候阻塞渲染(顯然腳本必須也被下載了)。

腳本下載常常被瀏覽器的預加載器所完成。

當瀏覽器頁面渲染被阻塞,即等待CSS,或JS被執行,預分析器將掃描頁面剩餘部分,尋找它能下載的資源。

有些瀏覽器如 Chrome, 將分先後下載資源,例如,如果腳本與圖片同時在等待下載,它將先下載腳本。

漂亮的內容!

所以,要使頁面被儘可能快的渲染,將styles放在頂部。爲了阻止JS的阻塞影響到渲染,將scripts放在底部。

更少的請求

另一個明顯而基本的性能優化方法是少下載。頁面需要的每一個資源就是一次額外的HTTP請求;瀏覽器不得不停下來去獲取每一個用於渲染頁面所需的資源。每一次HTTP請求都可能引發DNS查詢,重定向,404,等等。每一次HTTP請求,無論爲了樣式表,圖片,web字體,JS文件還是其它你能想到的,都可能是一次非常昂貴的操作。儘量減少這些請求是你可以做的最快的優化方法中的一種.

再談到瀏覽器和並行;大多數瀏覽器一次只從每個引用的域下載一些資源,而JS會阻塞這些下載。所以,你做的每一個HTTP請求都應該仔細考慮,而不是隨便隨便做的。

儘可能並行

爲了讓瀏覽器能並行的下載更多資源,你可以由不同的域名提供服務。如果說,瀏覽器只能一次從一個域名獲取兩個資源,那麼由兩個域名提供服務意味着它可以一次性獲取四個資源;三個域名意味着六個並行下載。

許多網站有靜態/資源 域名;你可以發現, Twitter, 用 si0.twimg.com 來做靜態資源:

<link rel="stylesheet" href="https://si0.twimg.com/a/1358386289/t1/css/t1_core.bundle.css" type="text/css" media="screen">

Facebook 用fbstatic-a.akamaihd.net:

<link rel="stylesheet" href="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yi/r/76f893pcD3j.css">

通過這些靜態的資源域名, Twitter與Facebook能提供更多的並行資源服務;來自twitter.com和si0.twimg.com的資源可以協作方式下載。這真的是使你的頁面上獲得更多併發下載的簡單方法,如果再加上實際的CDN技術就會更好,CDN技術通過從一個更加合適的物理位置提供資源服務的方法來減少延遲。

這全部都很好,但後面我們將討論在特定環境下,怎樣從子域名提供服務卻會實際上對性能有害。

因此,現在有了我們關於性能的基礎知識:

  • 將樣式表放在文檔的頂部
  • 將JavaScript放在底部(可能的地方)
  • 儘可能減少HTTP請求
  • 從多個域名提供資源服務能增加瀏覽器並行下載的資源數量。

HTTP 請求與 DNS 查詢

每當你從任何域名請求一個資源,會發出一個帶有相關頭部,被訪問資源的 HTTP請求,並且會返回一個響應。這是對該過程的一個極端簡化,但它基本就是事實上你需要知道的。這是一個HTTP請求,而且所有涉及的資源都從屬於這個往返的旅行。當提到前端性能,這些請求正是主要的瓶頸所在,因爲如我們談到的,瀏覽器受限於有多少請求可以並行發生。這也是爲什麼我們經常要使用子域名;以便允許這些請求在數個域名上發生,允許同時發生多得多數量的請求。

然而關於這還有個問題,DNS查詢。每次(從一個空緩存)一個新的域名被引用,HTTP請求會受制於一個耗時的DNS查詢(某個介於20到120毫秒之間的值),在DNS查詢中,發出的請求會查詢資源實際存在的地點;互聯網通過IP地址被綁定在一起,這些地址由DNS管理的主機名引用。

如果每個引用的新域名具有DNS查詢的前端代價,你必須確保這個代價確實是值得的。如果是一個小網站(例如像CSS魔法),那麼由子域名提供資源可能並不值得;相比執行多個域名的DNS查詢並將其並行化來說,從一個域名非並行的獲取若干資源,瀏覽器可能更快。

如果你或許有一打資源,你可能會考慮從一個子域名提供它們的資源服務;爲了更好的並行化那許多資源,額外的DNS查詢可能是值得的。如果說你有40個資源,可能將那些資源切分到兩個子域名是值得的;爲了由總數爲三個的域名提供你的網站服務,兩個額外的DNS查詢會是值得的。

DNS查詢代價很高,因此你需要決定什麼纔是對你的網站更合適的;承擔查詢的消耗或者只是由一個域名提供所有服務。

很重要的需要記得的是,比方說一旦HTML被請求於foo.com,對那個主機的DNS查詢就立即發生了,所以後續的任何對foo.com的請求不再受制於DNS查詢。

DNS 預取

如果你像我一樣想在網站上有一個Twitter小程序,還有網站分析,再也許一些網頁字體,那麼你必須要鏈接到一些其它域名,這意味着你將不得不引發DNS查詢。我的建議通常是,不要還沒有先適當的考慮性能影響就使用某個或任何一個小程序,但對於你認爲確實需要的,下面的將很有用……

因爲這些東西都存在於其它域名,比方說這就意味着你的網站字體CSS將會同你自己的CSS並行下載,從某種意義上說是一種好處,但是腳本將仍會阻塞(除非它們是異步的

事實上,這裏的問題是DNS查詢牽涉到了第三方域名。幸運的是,有一個相當快又簡單的辦法來加速這個過程:DNS預取。

DNS預取所做的恰恰就是憑證領餐(on the tin),它不能被簡單實現。比方說,如果你需要請求來自widget.foo.com的資源,那麼你可以通過簡單的在頁面的<head>裏先增加下面這個來預取那個主機的DNS:

<head>
    ...
    <link rel="dns-prefetch" href="//widget.foo.com">
    ...
</head>

那行簡單的內容將會告訴支持的瀏覽器去開始預取那個域名的DNS,這要稍稍早於它實際需要的時刻。它意味着DNS查詢過程,在瀏覽器<script>元素真正請求小程序的時候就已經在進行中了。這僅僅給瀏覽器增加了一個很小的開頭。

這種簡單的鏈接元素(就是我在CSS魔法上用到的)完全後向兼容,而且不會忽略性能影響。將它看作是性能提升增強吧!

延伸閱讀

資源預取

和DNS預取一樣,也可以順便對你的站點需要的其它資源進行預取。爲了弄清楚我們想要預取哪些資源, 首先我們需要了解瀏覽器通常會在什麼時候以什麼方式對資源發出請求。

CSS中引用的Web字體和圖片表現基本相同;瀏覽器在碰到需要它們的HTML時開始對它們進行下載。就和我在前面提到那樣,瀏覽器非常聰明,這又是一個例證。想象一下,瀏覽器一看到下面的CSS聲明就開始下載其中所引用的圖片:

.page--home         { background-image:url(home.jpg); }
.page--about        { background-image:url(about.jpg); }
.page--portfolio    { background-image:url(portfolio.jpg); }
.page--contact      { background-image:url(contact.jpg); }

如果瀏覽器不是等碰到需要這些圖片的HTML再下載它們,那麼訪問主頁就會立即下載所有這四個圖片。這會造成浪費,所以瀏覽器一定會確保在需要這些圖片時纔會開始下載它們。所以,這裏有個問題在於,圖片下載直到很晚纔會開始。

如果我們可以完全確認某個CSS圖片肯定會在每個頁面都會用到的話,我們就可以用個小把戲讓瀏覽器早早下載好這個圖片,無需等到讓瀏覽器碰到需要使用該圖片的HTML纔開始下載。想做到這一點也非常簡單,但所用的方法可能會有點糙,就看你怎麼弄了。

比較糙的方法和大多數笨拙的萬全之法類似,就是在每個頁面放置一個隱藏的<div>,在該div中使用帶有空的alt屬性的<img>標籤。我在CSS Wizardry項目中的精靈中就是這麼幹的;因爲我知道,每個頁面都要使用該精靈,所以我就通過在HTML中對其進行引用對它進行預取。瀏覽器處理內聯(inline)<img>的方式非常好,瀏覽器會早早地對它們進行預取,所以通過讓瀏覽器將我的精靈作爲HTML中的<img>進行載入,瀏覽器就可以在使用需要精靈的CSS之前將其下載好。通過首先在我的HTML中引用該精靈(隱藏起來的),我就能夠搶先把精靈下載好。

還有第二種方法比較“優雅”,但會讓人有些困惑。它和DNS預取的例子非常相似

<link rel="prefetch" href="sprite.png">

這會顯式地告訴瀏覽器,馬上開始預取我的精靈圖片,而不要考慮在它處理CSS時可能會做的任何決定。

令人感到困惑之處在於有兩篇文章似乎有不同的觀點;基於來自MDN的這篇文章,貌似這種預取指令只是示意瀏覽器僅在它空閒時有可能會對href所指的資源進行預取。然而,與此矛盾的是,來自Planet Performance的這篇文章貌似在說,如果瀏覽器支持rel=”prefetch”的話,它就一定會預取href中所指的資源,並沒有提及是否要在瀏覽器空閒時才進行預取。我在WebKit的Inpsector中的瀑布圖中所看到的情況是後者說得是對的,但是在打開Developer Tools的情況下(薛定諤測不準。。。)WebKit的表現及其怪異,我就觀察不到預取動作的情況了,這也就是我說,我無法100%保證我說的是對的。要是誰能解釋清楚這方面的情況,我將不勝感激。

我在前面說過,字體和圖片表現非常相似,上面所說的規則同樣也適用於字體文件,但你無法使用隱藏的<div>載入字體文件(你需要使用預取link)。

<link rel="prefetch" href="webfont.woff">

所以,基本可以這麼說,我們這裏所作的一切,只能算是讓瀏覽器提前下載資源的“小把戲”而已,耍了小把戲之後,在瀏覽器碰到要使用CSS的時候,其中所引用的資源就早已下載好了(或者至少已經在下載中了)。 漂亮極了!

延伸閱讀

CSS 與性能

許多建議說,如果你在使用資源域名,你應該由它們提供所有靜態資源服務;包括CSS,JS,圖片等等。

但是在工作中我們發現一件事,那就是你不應該由一個資源/子域名提供CSS服務…

還記得先前我們討論CSS塊渲染嗎?瀏覽器想儘可能快的獲得CSS,直到不能更快;CSS位於你的關鍵路徑。你的關鍵路徑是用戶頁面請求與之後實際看到頁面之間的必要的旅程。因爲它阻塞了渲染,所以CSS位於關鍵路徑,而JS和圖片不是。你會希望在關鍵路徑上儘可能快的加快這個旅程,這就意味着不能有DNS查詢。

實際工作中,我們搭建了一個網站,在某個階段性的環境中它由同一臺主機(如foo.com)提供資源服務,但到了使整個環境支持更加繁忙業務的時候,我們開始由s1.foo.com與s2.foo.com提供資源服務。這意味着所有的圖片,JS,CSS,字體等等都來自於不同的域名,由此便引起了DNS查詢。這裏的問題在於,由於空的緩存,爲了獲得CSS文件而需要執行DNS查詢,這實際上使得關鍵路徑速度徹底慢下來。我們的圖片大多數會模糊,這暗示着有理論上不應該有的延時;最佳實踐要求應該將資源分佈於在子域名上,對嗎?但不包括CSS。DNS查詢佔據了大量的時間,進而延遲了頁面的渲染。

因爲有這種渲染阻塞階段,CSS是性能最壞的敵人之一,正如Stoyan Stefanov闡述的那樣 。而且也很有必要注意到瀏覽器在它開始渲染頁面之前將下載所有的CSS。這意味着即使瀏覽器僅僅在屏幕上渲染頁面,也要請求print.css。任何只是基於一種媒體查詢的樣式表(如<link rel=”stylesheet” media=”screen and (min-device-width: 800px)” href=”desktop.css”>)都將會被下載,即使並不需要它們。

即便如此,Andy Davies 告知我WebKit實際上提高了CSS下載的優先級,以便只有渲染頁面需要的CSS先到達,而其他的樣式,如print.css儘可能的延遲。漂亮!

知道這些關於CSS的信息已經允許我們做出一些決定,這些決定全部基於CSS阻塞渲染,要全部被請求,以及它位於關鍵路徑的知識:

  • 永遠不要從一個固定/資源域名提供服務 因爲這會引起DNS查詢並進一步延遲渲染。
  • 先提供服務 因此瀏覽器可以繼續忙下去。
  • 合併它 因爲不管怎樣瀏覽器會獲取所有CSS,你最好將所有這些壓縮於一個HTTP請求。
  • 壓縮並簡化它 以便瀏覽器需要下載的少一些。
  • 緩存它的一切 以便上述的過程儘可能少的發生。

CSS位於關鍵路徑,因此你需要儘早先解決它,它阻塞渲染就意味着降低了用戶的性能體驗。 把CSS移到子域名會損害性能。

延伸閱讀

壓縮與簡化

對於你的文本資源,有兩個實在很簡單的事情是你能(而且也應該)做的;簡化他們移除任何註釋和空格,並且進一步的壓縮它們大小。

如果你想選擇其一,單獨的壓縮要比單獨的簡化更有效。然而,如果可能的話你應該兩個都做。

實施壓縮經常需要一點.htaccess詭計,但如我的好朋友 Nick Payne指出的,.htaccess實際上從服務端的觀點來看不是特別有性能;.htaccess評估每一個到達請求,因此實際它有很多開銷。

這取自 Apache 文檔 :

你應該完全避免使用.htaccess文件,如果你可以直接訪問http主服務器的配置文件的話。 使用.htaccess文件使你的Apache http server慢下來。任何你能包含進一個.htaccess文件的指令最好設置在一個字典 塊,因爲它具有同樣的效用並且有更好的性能。

如果你確實只是訪問.htaccess,那麼我不會擔心;這個開銷的代價通常無需關心。實際上通過.htaccess來壓縮實現起來很簡單。而簡化不是那麼容易,除非你有一個構建過程,或者用一些類似代碼工具套件,或者能直接編譯輸出最小化的預處理器。

有趣的是,我移動inuit.css到Sass的主要原因最初是——我可以方便的編譯一個簡化的版本。

簡化(Minification)最主要的部分是簡單的刪除空格與註釋;如果你在代碼中寫的註釋像我一樣多,那麼你確實需要縮減你的資源。

像任何壓縮算法一樣,壓縮(Gzip)將任何基於文本的輸入,基於重複的/可重複的字符串對其進行壓縮。通過gzip大多數代碼壓縮得很好,因爲所有代碼都有包含重複字符串的傾向;例如CSS中一遍又一遍的background-image,標籤中一遍又一遍的<strong>…

壓縮真的大量的壓榨掉資源的大小,你應該明確的啓用它。爲了能有規範的.htaccess片段,查閱 HTML5 樣板處理資源 。

壓縮內容引起大量的節約。在寫操作的時候,inuit.css 輸入有77k大小。壓縮以後只有5.52k。簡化與壓縮給我們節省了93%。而且因爲gzip對基於文本的資源工作的很好,你甚至可以壓縮可縮放矢量圖形(SVGs)和一些字體格式文件!

優化圖像

相比通過優化工具運行而言,我對優化圖像的藝術不是非常的知識廣博,但通過圖像自身,後加工來解決是一個相當有趣的話題。

Spriting (精靈)

如果想要一個性能優異的網站,Sprites(精靈,一種網頁圖片應用處理方式,它允許你將一個頁面涉及到的所有零星圖片都包含到一張大圖中去)是幾乎強制性的;在一個HTTP請求里加載一個大的圖片,而不是若干個請求若干個圖片。但是問題在於,不是所有的圖片都可以立即精靈化;可能你有一個圖標,需要將它作爲一個彈性寬度元素的背景圖像,但你顯然不能將其精靈化,因爲sprites對非固定尺寸元素不起作用。你通常只要在sprite表中的圖像周圍放許多空格,但這樣在sprite中浪費像素它們自己就影響到性能了

爲了解決特定元素的不可精靈化,我們需要一種稱爲“精靈元素”的東西。這基本是一個空元素,一般是一個<i>,它的核心工作就是保持空並且加載一個背景圖像。

在我創建Sky Bet時我用過這些,YouTube用它們, Facebook用它們, Jonathan Snook 有 一篇SMACSS的文章整個章節關於他們。

基本的前提是,如果因爲一個元素是流態的而不能精靈化,那麼你在它裏面放置一個空元素解決尺寸的問題,然後就可以精靈化了,例如:

<li>
    <a href="/profile/">
        <i></i> Profile
    </a>
</li>

這裏我們不能精靈化<li>或者<a>,所以我們在那裏有一個空的<i>,它替代加載圖標。這是關於性能我最喜歡的事情之一;你正將聰明的技術整合來改善頁面速度,卻仍然在使用傳統的“壞”標記。有趣的定西!

延伸閱讀

視網膜圖像

你不需要將所有都提高到視網膜級別。在標準分辨率下,同樣的圖像放大2倍將包含四倍數量的像素。四倍啊。然而這並不意味着需要通過連接傳輸四倍的文件大小——感謝圖片自身的編碼格式——也就是說一旦圖像解壓並在瀏覽器中渲染,有四倍數量於平常的像素需要存儲於內存。

如果停下來思考一下;視網膜圖像最常(即使不是總是)需要用於給手機提供一個保真的UI。手機內存比其他設備少很多。視網膜效果給內存並不很多的設備提供了消耗內存的圖像……反覆全面的考慮一下你是真的需要視網膜圖像,或者還是你可以做出一個明智的妥協?

視網膜效果是一種很棒的,清晰的體驗,但如果需要5秒鐘時間下載的話,將不會有清爽的體驗。在大多數情形速度要勝過美感。

爲了給每個人提供足夠好的圖片,你可以很聰明的給所有設備提供1.5倍的圖像,但就我的觀點——最好的選擇是節儉的使用視網膜。

如果統計數據表明有足夠富餘,你就可以針對矢量圖形優化,或者用字體圖標代替位圖。在CSS魔法網站我使用了矢量圖形,這給我帶來了如下好處:

  • 分辨率無關
  • 可簡化
  • 可壓縮

在工作中 Matt Allen 給我們做了一種字體圖標,可以同主要元素一起使用提供一種準視網膜的,可伸縮的圖標。

你也可以看看怎樣使用類似ReSRC.it的服務,以便基於設備與上下文加載圖片。

漸進的 JPGs

性能的一個有趣的方面是感知性能;不是非要你的數字告訴你,而是一個站點感覺起來有多快。

當顯示大的JPG圖像時,可能你對它的一頓一頓的下載再熟悉不過;圖像傳輸一百個像素,停頓,再五十個,停頓,突然一下子再兩百個像素,整個圖像加載完畢。

這是JPG圖像的傳統的工作基準,真的是一種非常卡的體驗。通過切換到漸進的JPGs,你能使它們以一種更優異的流行方式加載;它們首次顯示的是整個圖像,但像素不是很清晰,然後慢慢的聚焦。這聽起來比前面的方法要糟糕,但它感覺起來更快;用戶立即就有東西可看,而且圖像的質量逐漸的提高。典型的這些圖像要比它們的基準副本大一點,但是卻使整個體驗感覺快了許多。

啓用漸進的JPGs,你只要簡單的在Photoshop中爲web與設備保存圖像時,檢查一下相關的複選項;完工!

延伸閱讀

 

完全不用圖片

比spriting、可縮放矢量圖形、以及避免視網膜效果更好的是避免所有圖片。如果你能用圖片100%複製一個設計,但是用純CSS 75%複製,那麼寧可用純CSS解決方案(當然如果它不會最終超過100行的代碼的話!)避免圖片意味着避免了可能的HTTP請求,但有助於維護。如果你能避免使用圖片那麼試着那麼做吧。

總結

因此我們有了一些 (但仍然只有一點) 事情可以去做,以此來挖掘瀏覽器潛力並使你的前端更快速。知道一些關於瀏覽器如何工作的知識,確實可以讓我們進一步控制它並使前端更快。

如果你有任何補充——或真的不同意或想修正——那麼請加入分論壇的討論 ;性能的世界對我仍然相對較新,因此我期待從別人那裏學習,並將一切進一步深入。

我真的希望這版文字能夠至少某種程度啓發到你,使你遇到你可能從未想過的一些新知識。我也希望,如果你沒有準備好,這篇文章可以幫助你至少獲得我發現的性能之中的樂趣的二分之一。

我要特別感謝Nick Payne 和 Andy Davies幫助澄清了一些寫作本文時遇到的一些問題;謝了夥計們!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章