Medium.com 的前端技術基於什麼,有怎樣的特點?

Medium

簡單分析:伺服器可以完成渲染,並且渲染後的首頁會同時包括 feed 的 JSON 內容;瀏覽器同樣可以通過 JSON 數據渲染。文章同樣道理。

JavaScript 經過某種特定優化,未混淆。類似 ASM.js?https://dnqgz544uhbo8.cloudfront.net/_/fp/js/main-base.bundle.DImGiPCYnH-gW2A2BX9zag.js

求更多具體分析和內幕。


Medium 用了三個和知乎同樣在使用的 Closure 相關工具,不得不說在 Google 之外很少見:

  • Closure Library:提供定義和引用模塊的方式,以及一些 UI 組件(比如編輯器,Medium 在此之上做成了無形的,真是引人注目)。

  • Closure Compiler:前端代碼進行優化壓縮的工具。

  • Plovr:Closure Library 專用的模塊分組打包工具。

顧鵬說的渲染方式有點不對,pjax 和 SPA 的技術幾乎是對立的。Medium 是一個採用了 RESTful API 的 web app,它沒有直接替換 HTML,而是用 JSON + 模板來生成 HTML。我認為 Medium 在伺服器端和客戶端共用了模板,因此它能進行一致的渲染。當首屏載入時使用伺服器端渲染,之後的瀏覽使用客戶端渲染。這樣做的優點是對 SEO 友好,載入速度更快——首屏內容裡面有個 embedded 欄位,包含了文章的 JSON 表示,這節省了一個 HTTP 請求。文章對象裡面有個有趣的地方是, HTML 的結構化內容全部用被 JavaScript 化了——精確到任何塊級或內聯元素—— 這樣每個段落都可以由用戶添加註釋。

Medium 還有一篇文章分享了許多技術細節:Just a web page?

糾正,感謝顧鵬提供的文章鏈接,觀察了一些請求之後,發現 Medium 在文章頁面點擊左側菜單中的 Home 鏈接時,使用了 pjax 載入首頁的文章列表:

Request URL:https://medium.com/?format=fragment
Request Method:GET
X-Response-Type:html-body

補充文章載入過程:

# 載入主資源,文章內容:
Request URL: https://medium.com/p/:postid
Request Method: GET
Accept: application/json

# 載入次級資源,此時文章內容已經提前顯示,沒有 side-loading:
GET https://medium.com/p/:postid/follow-ups
GET https://medium.com/p/:postid/upvotes
GET https://medium.com/p/:postid/notes

# 中途滾動,記錄當前正在閱讀的章節:
PUT https://medium.com/p/:postid/state/location
Request Payload: {paragraphName: "f169", sectionName: "0c50"}

# 滾動到頁面底端,標記為已讀:
PUT https://medium.com/p/:postid/state/read

# 滑鼠經過(不需要點擊)在底端的導航推薦文章鏈接之上時,預載其內容,並渲染成 DOM:
GET https://medium.com/:collection/:postid
Accept: application/json

最後一步預載內容就是出現多個 surface div 的原因,Medium 做的優化,以方便快速地切換頁面。

附上 DOM 斷點截圖(含 post 對象和模板):


Update:

來看看Medium是如何load新文章的。

1. 新的頁面:

&
&...& &

2. 滾到文章底部後html更新了:

&
&...& &...& &

// Ajax獲取了新的div,並插入到container中,div的display是none.

3. Load新的文章後:

&
&...& &

// 原文章的div給刪掉了。同時,使用pushState更新URL。

看出來了么,新的文章從來不是一個新的html頁面,只是用新的div替換了原來的div。

&<刪除&>個人認為,pjax是個極好的技術,特別是對於SPA來說。直接在server端用templates進行渲染,傳給前端html,提高前端的性能。&<刪除&>

感謝長天之雲指正,Medium其實是用ajax + json + 模板,html是在前端渲染,再插入到container中;並非是用pjax直接從server獲得渲染後的html。

--------

首先,Medium 是 Single Page Application (SPA)。SPA的好處是載入新頁面的時候,瀏覽器不需要重新解析js、css和字體文件。不好滿,就是需要一些技術解決搜索引擎索引,瀏覽器前進後退按鈕功能....

## 渲染方式

實際上,Medium使用了好幾種方式進行渲染:

1. 對於不需要被搜索引擎索引的頁面,直接交給瀏覽器端進行渲染;

2. 使用pjax技術,也就是pushState+Ajax,後台只渲染頁面一部分,然後前台通過Ajax動態抓取html,插入到該放的位置。

3. 有些數據可能會在好幾個地方需要用到,對於這種情況,後台把渲染好的html和這些數據以json格式一起發給前端,前台直接用html更新UI,把數據存儲起來以備後用。這樣避免了重新請求數據。

## 前台技術

他們的招聘介紹里有提到。Medium用的是Closure和Less。

另外,他們自己寫了一個MVC的framework - matador。根據matador的描述 "Uses SoyNode to render Closure Templates",他們用SoyNode來渲染Closure的templates。soyNode是他們的另一個開源項目。matador裡面也用到很多第三方的庫,具體哪些自己去看看。

## 後台技術

基本是NodeJS + Nginx。非常依賴AWS,也有用Redis。

具體的可以去看他們的職位介紹,和開源項目。

Rise of the SPA

Front End Engineer

Obvious/matador · GitHub

Obvious/open-source · GitHub

---

Peng


推薦閱讀:

請教變數作用域問題,下面代碼彈出的為什麼不是global?
如何為js的eval指定一個object作為上下文?
求解一小段代碼,完全不理解eval的作用機制?
有關JS中作用域的問題,全局作用域中的變數不應該在全局變數對象中嗎,為什麼下面的函數搜索不到全局變數?
為什麼ECMAScript不原生支持重載?

TAG:前端開發 | JavaScript | Medium |