譯自 CyberAgent Developers Blog
原文標題 「アメブロ2016 ~ React/ReduxでつくるIsomorphic web app ~」
作者 HERABLOG
大家好瘟忱,我是原一成(@herablog),目前在CyberAgent主要擔任前端開發(fā)。
Ameblo(注: Ameba博客驯妄,Ameba Blog奏黑,簡稱Ameblo)于2016年9月堪侯,將前端部分由原來的Java架構(gòu)的應用湾揽,重構(gòu)成為以node.js茵瘾、React為基礎(chǔ)的Web應用饵婆。這篇文章介紹了本次重構(gòu)的起因勺馆、目標、系統(tǒng)設(shè)計以及最終達成的結(jié)果。
新系統(tǒng)發(fā)布后草穆,立即就有人注意到了這個變化灌灾。
系統(tǒng)重構(gòu)的起因
2004年起,Ameblo成為了日本國內(nèi)最大規(guī)模的博客服務悲柱。然而隨著系統(tǒng)規(guī)模的增長锋喜,以及很多相關(guān)人員不斷追加各種模塊、頁面引導鏈接等豌鸡,最終使得頁面展現(xiàn)緩慢嘿般、對網(wǎng)頁瀏覽量(PV)造成了非常嚴重的影響。并且頁面展現(xiàn)速度方面直颅,絕大多數(shù)是前端的問題博个,并非是后端的問題。
基于以上這些問題功偿,我們決定以提高頁面展現(xiàn)速度為主要目標盆佣,對系統(tǒng)進行徹底重構(gòu)。與此同時后端系統(tǒng)也在進行重構(gòu)械荷,將以往的數(shù)據(jù)部分進行API化改造共耍。此時正是一個將All-in-one的巨型Java應用進行適當分割的絕佳良機。
目標
本次系統(tǒng)重構(gòu)確立了以下幾個目標吨瞎。
頁面展現(xiàn)速度的改善(總之越快越好)
用于測定用戶體驗的指標有很多痹兜,我們認為其中對用戶最重要的指標就是頁面展現(xiàn)速度。頁面展現(xiàn)速度越快颤诀,目標內(nèi)容就能越快到達字旭,讓任務在短時間內(nèi)完成。這次重構(gòu)的目標是盡可能的保持博客文章崖叫、以及在Ameblo內(nèi)所呈現(xiàn)的繁多的內(nèi)容的固有形式遗淳,在不破壞現(xiàn)有價值、體驗的基礎(chǔ)上心傀,提高展現(xiàn)和頁面行為的速度屈暗。
系統(tǒng)的現(xiàn)代化(搭乘生態(tài)系統(tǒng))
從前的Web應用是將數(shù)據(jù)以HTML的形式返回,那個時候并沒有什么問題脂男。然而养叛,隨著內(nèi)容的增加,體驗的豐富化宰翅,以及設(shè)備的多樣化弃甥,使得前端所占的比重越來越大。此前要開發(fā)一個好的Web應用汁讼,如果要高性能潘飘,就一定不要將前后端分隔開肮之。當年以這個要求開發(fā)的系統(tǒng),在經(jīng)歷了10年之后卜录,已經(jīng)遠遠無法適應當前的生態(tài)系統(tǒng)戈擒。
「跟上當前生態(tài)系統(tǒng)」,以此來構(gòu)建系統(tǒng)會帶來許許多多的好處艰毒。因為作為核心的生態(tài)系統(tǒng)筐高,其開發(fā)非常活躍丑瞧,每天都會有許許多多新的idea柑土。因而最新的技術(shù)和功能更容易被吸納,同時實現(xiàn)高性能也更加容易绊汹。同時稽屏,這個「新」對于年輕的技術(shù)新人也尤為重要。僅懂得舊規(guī)格舊技術(shù)的大叔對于一個優(yōu)秀的團隊來說是沒有未來的(自覺本人膝蓋也中了一箭)西乖。
升級界面設(shè)計狐榔、用戶體驗(2016年版Ameblo)
Ameblo的手機版在2010年經(jīng)歷了一次改版之后,就基本上沒有太大的變化获雕。這其間很多用戶都已經(jīng)習慣了原生應用的設(shè)計和體驗薄腻。這個項目也是為了不讓人覺得很土很難用,達到順應時代的2016年版界面設(shè)計和用戶體驗届案。
OK庵楷,接下來讓我具體詳細聊聊。
頁面加載速度的改善
改善點
系統(tǒng)重構(gòu)前楣颠,通過 SpeedCurve 進行分析尽纽,得出了下面結(jié)論:
- 服務器響應速度很快
- HTML文檔較大(頁面所有要素都包含其中)
- 阻塞頁面渲染的資源(JavaScript、Stylesheet)較多
- 資源讀取的次數(shù)過多童漩,體積過大
依據(jù)這些確定了下面這幾項基本方針:
- 為了不致于降低服務器響應速度弄贿,對代碼進行優(yōu)化,緩存等
- 盡可能減少HTML文檔大小
- JavaScript異步地加載與執(zhí)行
- 最初呈現(xiàn)頁面時睁冬,僅僅加載所需的必要資源
SSR還是SPA
近年來相比于添加到收藏夾中,用戶更傾向于通過搜索結(jié)果看疙、Facebook豆拨、Twitter等社交媒體上的分享鏈接打開博客頁面。Google和Twitter的AMP, Facebook的Instant Article表明第一頁的展現(xiàn)速度極大影響到用戶滿意度能庆。
此外施禾,從Google Analytics等日志記錄中了解到在文章列表頁面和前后文章間進行跳轉(zhuǎn)的用戶也很多。這或許是因為博客作為個人媒體搁胆,當某一用戶看到一篇不錯的文章弥搞,非常感興趣的時候邮绿,他也同時想看一看同一博客內(nèi)的其它文章。也就是說攀例,博客這種服務 第一頁快速加載與頁面間快速跳轉(zhuǎn)同等重要 船逮。
因此,為了讓兩者都能發(fā)揮最佳性能粤铭,我們決定在第一頁使用服務器端渲染(Server-side Rendering, SSR)挖胃,從第二頁起使用單頁面應用(Single Page Application, SPA)。這樣一來梆惯,既能確保第一頁的展示速度和機器可讀性(Machine-Readability)(含SEO)酱鸭,又能獲得SPA帶來的快速展示速度。
BTW垛吗,對于目前的架構(gòu)凹髓,由于服務器和客戶端使用相同的代碼,全部進行SSR或是全部進行SPA也是可能的怯屉。目前已經(jīng)實現(xiàn)即便在不能運行JavaScript的環(huán)境中蔚舀,也可以正常通過SSR來瀏覽∈粗可以預見將來等到Service Worker普及之后蝗敢,初始頁面將更加高速化,而且可以實現(xiàn)離線瀏覽足删。
以前的系統(tǒng)完全使用SSR寿谴,而現(xiàn)在的系統(tǒng)從第二頁起變?yōu)镾PA。
SPA的魅力在于呈現(xiàn)速度之快失受。因為僅僅通過API獲取所需的必要數(shù)據(jù)讶泰,所以速度非常快拂到!
延遲加載
我們使用SSR+SPA的方法來優(yōu)化頁面間跳轉(zhuǎn)這種橫向移動的速度痪署,并且使用延遲加載來改善頁面的縱向移動速度。一開始要展現(xiàn)的內(nèi)容以及導航兄旬,還有博客文章等最早呈現(xiàn)狼犯,在這些內(nèi)容之下的次要內(nèi)容隨著頁面的滾動逐漸呈現(xiàn)。這樣一來领铐,重要的內(nèi)容不會受頁面下面內(nèi)容的影響而更快的顯示出來悯森。對于那些想盡快讀文章的用戶來說,既不增加用戶體驗上的壓力绪撵,又能完整的提供頁面下方的內(nèi)容瓢姻。
之前的系統(tǒng)因為將頁面內(nèi)的全部內(nèi)容都放到HTML文檔里,所以使得HTML文檔體積很大音诈。而現(xiàn)在的系統(tǒng)幻碱,僅僅將主要內(nèi)容放到HTML里返回绎狭,減少了HTML的體積和數(shù)據(jù)請求的大小。
HTML緩存
博客文章是靜態(tài)文檔褥傍,對于特定URL的請求會返回固定的內(nèi)容儡嘶,因此非常適合進行緩存。緩存使得服務器處理內(nèi)容減少摔桦,在提高頁面響應速度的同時減輕了服務器的負擔社付。我們將不變的內(nèi)容(文章等)生成的HTML進行緩存返回,對于由于變化的內(nèi)容能過JavaScript邻耕、CSS等進行操作(比如顯示鸥咖、隱藏等)。
這張圖顯示了2016年9月最后一周New relic上的統(tǒng)計數(shù)據(jù)兄世。文章列表頁面的HTML的響應時間基本在50ms以下啼辣。
這張圖是文章詳細頁面的統(tǒng)計數(shù)據(jù)∮玻可以看出鸥拧,這個頁面的響應時間也基本上是在50ms以下。由于存在文章過長的時候會造成頁面體積變大削解,以及文章頁面不能完全緩存等情況富弦,所以相比列表頁面會存在更多較慢的響應。
對于因請求的客戶端而產(chǎn)生變化部分的處理氛驮,我們在HTML的body標簽中通過加入相應的class腕柜,然后在客戶端通過JavaScript和CSS等進行操作。比如矫废,一些內(nèi)容不想在某些操作系統(tǒng)上顯示盏缤,我們就用CSS對這些內(nèi)容進行隱藏。由于CSS樣式表會先載入蓖扑,頁面布局確定下來之后再進行頁面渲染唉铜,所以這個也可以解決后面要提到的「咯噔」問題。
<!-- html -->
<body class="OsAndroid">
/* main.css */
body.OsAndroid .BannerForIos {
dsplay: none;
}
系統(tǒng)的現(xiàn)代化(搭乘生態(tài)系統(tǒng))
技術(shù)選型
這次項目的技術(shù)選擇時律杠,遵循了盡可能采用當前當前市場上已經(jīng)存在的普遍使用的技術(shù)這一原則潭流。暗號就是:「活脫脫像范例應用一樣Start」。這樣一來柜去,無論是誰都可以輕松的獲取到相應的文檔等信息灰嫉,同時其它的團隊和公司如果要參與到項目中來也能很快的上手。然而在真正進行開發(fā)的時候诡蜓,一些細節(jié)實現(xiàn)上因為各種各樣的原因存在一些例外的情況熬甫,但是在極大程度上保持了各個模塊的獨立性胰挑。最終系統(tǒng)的大體構(gòu)成如下圖所示:
(有些地方做了省略)
React with Redux
使用React和React進行開發(fā)的的時候蔓罚,很多地方可以用 純函數(shù) 的形式進行組合椿肩。純函數(shù)是指特定的參數(shù)總是返回特定的結(jié)果,不會對函數(shù)以外的范圍造成污染豺谈。使用純函數(shù)進行開發(fā)可以保證各個處理模塊最小化郑象,不用擔心會無意間改變引用對象的值。這樣一來茬末,十分有助于大規(guī)模開發(fā)以及在同一客戶端中維持多個狀態(tài)厂榛。
界面更新的流程是: Action(Event) -> Reducer (返回新的state(狀態(tài))) -> React (基于更新后的store內(nèi)的state更新顯示內(nèi)容)
。
這是一個Redux Action的例子丽惭,演示了React Action (Action Creator) 基于參數(shù)返回一個Plain Object击奶。處理異步請求的時候,我們參考 官方文檔 责掏,分別定義了成功請求和失敗請求柜砾。獲取數(shù)據(jù)時使用了 redux-dataloader 。
// actions/blogAction.js
export const FETCH_BLOG_REQUEST = 'blog/FETCH_BLOG/REQUEST';
export function fetchBlogRequest(blogId) {
return load({
type: FETCH_BLOG_REQUEST,
payload: {
blogId,
},
});
}
Redux Reducer是一完全基于Action中攜帶的數(shù)據(jù)换衬,對已有state進行復制并更新的函數(shù)痰驱。
// reducers/blogReducer.js
import as blogAction from '../actions/blogAction';
const initialState = {};
function createReducer(initialState, handlers) {
return (state = initialState, action) => {
const handler = (action && action.type) ? handlers[action.type] : undefined;
if (!handler) {
return state;
}
return handler(state, action);
};
}
export default createReducer(initialState, {
[blogAction.FETCH_BLOG_SUCCESS]: (state, action) => {
const { blogId, data } = action.payload;
return {
...state,
[blogId]: data,
};
},
});
React/Redux基于更新后的store中的數(shù)據(jù),對UI進行更新瞳浦。各個組件依據(jù)傳遞過來的props值担映,總是以相同的結(jié)果返回HTML。React將View組件也作為函數(shù)來對待叫潦。
// main.js
<SpBlogTitle blogTitle="渋谷のブログ" />
// SpBlogTitle.js
import React from 'react';
export class SpBlogTitle extends React.Component {
static propTypes = {
blogTitle: React.PropTypes.string,
};
shouldComponentUpdate(nextProps) {
return this.props.blogTitle !== nextProps.blogTitle;
}
render() {
return (
<h1>{this.props.blogTitle}</h1>
);
}
}
有關(guān)Redux的信息在 官方文檔 中說明得非常詳細蝇完,推薦隨時參考一下這個文檔。
同構(gòu)Web應用(Isomorphic web app)
Ameblo 2016年版基本上完全是用JavaScript重寫的诅挑。無論是Node服務器上還是客戶端上都使用了相同的代碼和流程四敞,也就是所謂的同構(gòu)Web應用。項目的目錄結(jié)構(gòu)大體上如下所示拔妥,服務器端的入口文件是 server.js
忿危,瀏覽器的入口文件是 client.js
。
-
actions/
Redux Action (服務器没龙,客戶端共用) -
api/
封裝的API接口 -
components/
React組件 (服務器铺厨,客戶端共用) -
reducer/
<span class="underline">Redux Reducers</span> (服務器,客戶端共用) -
services/
服務層模型硬纤,使用 Fetchr 對數(shù)據(jù)請求進行適當粒度的劃分解滓。同時這個也使得node.js作為代理,間接請求API(服務器專用)筝家。 -
server.js
服務器入口(服務器專用) -
app.js
node服務器的配置洼裤、啟動,由server.js調(diào)用(服務器專用) -
client.js
客戶端入口(客戶端專用)
寫好的JavaScript同時運行在服務器端還是客戶端上的運行行為溪王、以及從數(shù)據(jù)讀取直到在頁面上顯示為止的整個瀏程腮鞍,都以相同的形式進行值骇。
使用Github的語言統(tǒng)計可以看出 ,JavaScript占了整個項目的94.0%移国,幾乎全部都是由JavaScript寫成的吱瘩。
原子設(shè)計(Atomic Design)
對于組件的規(guī)劃鞍泉,我們采用了 原子設(shè)計 理念纺念。其實項目并沒有一開始就采用原子設(shè)計丢郊,而是根據(jù) Presentational and Container Components 扎狱,對 container
和 component
進行了兩層劃分砚嘴。然而Ameblo中的組件實在是太多聪铺,很容易造成職責不明確的情況亥揖,因此最終采用了原子設(shè)計理念妙同。項目的實際運用中砚蓬,采用了以下的規(guī)則兄朋。
Atoms
組件的最小單位,比如Icon怜械、Button等颅和。原則上不具有狀態(tài),從父組件中獲取傳遞過來的props缕允,并返回HTML峡扩。
Molecules
以復用為前提的組件,比如List障本、Modal教届、User thunmbnail等。原則上不具有狀態(tài)驾霜,從父組件中獲取傳遞過來的props案训,并返回HTML。
Organisms
頁面上較大的一塊組件粪糙,比如Header强霎,Entry,Navi等蓉冈。對于這一層的組件城舞,可以在其中進行數(shù)據(jù)獲取處理,以及使用Redux State 和 connect 寞酿,維護組件的狀態(tài)家夺。這里獲取的組件狀態(tài)以props的形式,傳遞給 Molecules
和 Atom
伐弹。
// components/organisms/SpProfile.js
import React from 'react';
import { connect } from 'react-redux';
import { routerHooks } from 'react-router-hook';
import { fetchBloggerRequest } from '../../../actions/bloggerAction';
// 數(shù)據(jù)獲取處理 (使用react-router-hook)
const defer = async ({ dispatch }) => {
await dispatch(fetchBloggerRequest());
};
// Redu store的state作為props
const mapStateToProps = (state, owndProps) => {
const amebaId = owndProps.params.amebaId;
const bloggerMap = state.bloggerMap;
const blogger = bloggerMap[amebaId];
const nickName = blogger.nickName;
return {
nickName,
};
};
@connect(mapStateToProps)
@routerHooks({ done })
export class SpProfileInfo extends React.Component {
static propTypes = {
nickName: React.PropTypes.string.isRequired,
};
render() {
return (
<div>{this.props.nickName}</div>
);
}
}
Template
各個請求路徑(URL)所對應的組件拉馋。其職責是將所需的部件從Organisms中import過來,以一定的順序和格式整合在一起。
Pages
作為頁面的頁面組件煌茴∧眩基本上是把傳遞過來的 this.props.children
原原本本的顯示出來。由于Ameblo是單頁面應用景馁,因而只有一個頁面組件。
CSS Modules
CSS樣式表使用 CSS Modules 將CSS樣式規(guī)則的作用范圍嚴格限制到了各個組件內(nèi)逗鸣。各個樣式規(guī)則的作用范圍進行限制使得樣式的變更和刪除更加容易合住。因為Ameblo是由許多人協(xié)同開發(fā)完成,不一定每個人都精通CSS撒璧,而且不免要時常對一些不知是誰何時寫的代碼進行更改透葛,在這個時候?qū)⒆饔梅秶拗频浇M件的CSS Modules就發(fā)揮其作用了。
/ components/organisms/SpNavigationBar.css /
.Nav {
background: #fff;
border-bottom: 1px solid #e3e5e4;
display: flex;
height: 40px;
width: 100%;
}
.Logo {
text-align: center;
}
// components/organisms/SpNavigationBar.js
import React from 'react';
import style from './SpNavigationBar.css'
export class SpBlogInfo extends React.Component {
render() {
return (
<nav className={style.Nav}>
<div className={style.Logo}>
<img
alt="Ameba"
height="24"
src="logo.svg"
width="71"
/>
</div>
<div ...>
</nav>
);
}
}
各個class的名稱經(jīng)過webpack編譯之后卿樱,變成像 SpNavigationBar__Nav___3g5MH
這樣含hash值的全局唯一名稱僚害。
ESLint, stylelint
這次的項目將ESLint和stylelint放到了必須的位置,即便一個字母出錯繁调,整個項目也無法測試通過萨蚕。目的就在于統(tǒng)一代碼風格,節(jié)約代碼審查時的麻煩蹄胰。具體規(guī)則分別繼承自 eslint-config-airbnb 和 stylelint-config-standard 岳遥,對于一些必要的細節(jié)做了少許定制。因為規(guī)則較嚴裕寨,起初的時候或許有點不便浩蓉。新成員加入項目組時,代碼通過Lint測試便成了要通過的第一關(guān)??宾袜。
防止了代碼審查時對于這些細微寫法挑錯捻艳。被機器告知錯誤時,心理上會感覺稍好一些庆猫。
加入項目組之后认轨,最初的這段時間里發(fā)生Lint錯誤是常有的事。
CI, Build, Tesing
代碼的 構(gòu)建 月培、測試 和 部署 統(tǒng)一使用CI(公司內(nèi)部使用 CircleCI )來完成好渠。各個分支向GHE(Github Enterprise)PUSH之后,依據(jù)各個分支產(chǎn)生不同的動作节视。這個流程的好處就是構(gòu)建相關(guān)的處理不需要專門人員來完成拳锚,而是統(tǒng)一寫在 circle.yml
和 package.json
(node環(huán)境下)里。
-
develop
開發(fā)(下次發(fā)布)用分支寻行。構(gòu)建霍掺、測試之后自動部署到staging環(huán)境中。 -
release/vX.X.X
發(fā)布分支。由develop分支派生杆烁,構(gòu)建牙丽、測試之后,自動部署到semi(準生產(chǎn))環(huán)境中兔魂。 -
hotfix/vX.X.X
hotfix分支烤芦。由master分支派生,構(gòu)建析校、測試之后构罗,自動部署到semi(準生產(chǎn))環(huán)境中。 -
deploy/${SERVER_NAME}
部署到分支所指定的相應服務器上智玻。主要是在開發(fā)環(huán)境中使用遂唧。 -
master
這個分支構(gòu)建之后生成可以用于部署到production(生產(chǎn))環(huán)境的docker鏡像。 -
其它
開發(fā)用分支吊奢。僅進行構(gòu)建和測試盖彭。
Docker
本次系統(tǒng)重構(gòu),也對node.js應用進行docker化構(gòu)建页滚。這次重構(gòu)的是前端系統(tǒng)召边,我們希望可以在細小修正之后立即進行部署。docker化之后裹驰,一旦將鏡像構(gòu)建完成掌实,可以不受node模塊版本的左右進行部署,回滾也很容易邦马。
此外贱鼻,node.js本身發(fā)布非常頻繁,如果放置不管滋将,不知不覺之間系統(tǒng)就成古董了邻悬。docker化之后,可以不受各主機環(huán)境的影響自由的進行升級随闽。
更重要的是父丰,設(shè)置docker容器數(shù)是比較容易的,這對于系統(tǒng)橫向擴容以及對服務器配置作優(yōu)化時也十分方便掘宪。
升級界面設(shè)計蛾扇、用戶體驗(2016年版Ameblo)
不再「咯噔」
系統(tǒng)重構(gòu)之前的Ameblo由于存在一些高度沒有固定的模塊,出現(xiàn)了「咯噔」現(xiàn)象魏滚。這種「咯噔」會導致誤點擊以及頁面的重繪镀首,十分令人厭煩。而此模塊高度固定也做為本次系統(tǒng)重構(gòu)的UI設(shè)計的前提鼠次。特別是頁面間導航作為十分重要的元素更哄,我們經(jīng)過努力使得在頁面跳轉(zhuǎn)時每次都可以觸擊到相同的位置芋齿。
「咯噔」的一個例子。點擊[次のページ](下一頁)的時候成翩,額外的元素由于加載緩慢觅捆,造成誤點擊。
系統(tǒng)重構(gòu)之后麻敌,元素的位置被固定下來栅炒,減輕了頁面跳轉(zhuǎn)時給用戶心理上帶來的負擔。
智能手機時代的用戶界面
2016年在移動環(huán)境下使用的用戶幾乎都在使用智能手機术羔。在智能手機上赢赊,由于各個平臺的提供者制定了各自不同的用戶界面規(guī)范,用戶已經(jīng)習慣并適應了用戶界面聂示。相比之下,雖說瀏覽器上的規(guī)范非常少簇秒,但是如果和當今流行的界面差距太大的話鱼喉,就會變得很難用。
Ameblo的手機版在2010年進行改版之后趋观,自然對一些細節(jié)進行了改善扛禽,但是由于沒有太大的變動,所以現(xiàn)在看來很多地方已經(jīng)給人一種很舊的印象皱坛。用戶在瀏覽的時候编曼,對于界面并不區(qū)別是原生應用還是瀏覽器,因而制作出適應當前時代這個平臺的用戶界面顯得尤為重要剩辟。這里介紹一下本次重構(gòu)中掐场,對于界面的一些升級。
內(nèi)容占據(jù)界面上橫向整個空間贩猎。2010年的時候熊户,一般采用Twitter倡導的「將各個模塊圈起來的設(shè)計」。
增加了導航欄吭服,把導航相關(guān)操作集中放置在這里嚷堡。
可訪問性
這次系統(tǒng)重構(gòu)正值可訪問性成為熱點話題的時候。仔細的為HTML增加相當標簽屬生就可以使整個系統(tǒng)足夠可訪問艇棕。首先在HTML標簽屬性添加上時要用心斟酌蝌戒。對于標題、 img
等添加適當?shù)?alt
屬性沼琉,對于可點擊的元素一定要使用 a
button
等可點擊的標簽北苟。如果能自動對可訪問性進行檢驗就再好不過了,ESlint的 jsx-a11y 插件可以幫助完成這一點打瘪。
在項目進行的時候粹淋,正好公司內(nèi)開展了一次可訪問性的學習活動( Designing Web Accessibility 的作者太田先生和伊原先生也參加了這次活動)吸祟,在這次活動上也嘗試了Ameblo到目前為止沒有注意過的語音朗讀器。當時用語音朗讀器在Ameblo上進行朗讀時桃移,有幾處有問題的地方屋匕,使用 WAI-ARIA 對這幾處加以修正(與 data-*
相同,JSX也支持 aria-*
屬性)借杰。
這里 的PPT中有詳細的介紹过吻,歡迎閱覽(日文)。
結(jié)果
OK蔗衡,上面介紹了本次重構(gòu)帶來的很多變化纤虽,那么結(jié)果如何呢?
首先是性能相關(guān)指標(測試的URL都是Ameblo中單一頁面請求資源最多绞惦,展示速度最慢的頁面)逼纸。
阻塞渲染的資源(Critical Blocking Resources)
阻塞渲染的資源數(shù) 減少了75% !JavaScript全部變成了異步讀取與執(zhí)行济蝉。CSS樣式因為運營的原因杰刽,維持了重構(gòu)前的狀態(tài)。
內(nèi)容請求(Content Requests)
資源請求數(shù) 減少了58.04% 王滤!由于使用了延遲加載贺嫂,首屏顯示只加載必要的資源,與此同時對文件進行適當?shù)恼硌阆纾h除了一些不必要的模塊第喳,最終達成了這個狀態(tài)。
渲染(Rendering)
渲染速度做為前端的主要性能指標踱稍,本次 提升了44.68% 曲饱。
頁面加載時間(Page Load Time)
頁面加載時間 縮短了40.5 !此外珠月,后端的返回時間也維持在了0.2ms ~ 0.3ms之間渔工。
接下來介紹一下相關(guān)的業(yè)務指標。
網(wǎng)頁瀏覽量(Pageviews)
因為2016年9月有一位有名的博客主成為了熱點話題桥温,所以這個指標內(nèi)含有特殊情況引矩。網(wǎng)頁瀏覽量提升了57.15%。如果將熱點話題所帶來的數(shù)值除去后侵浸,實際上單純由系統(tǒng)重構(gòu)所帶來的提升在10%到20%之間旺韭。
每次會話瀏覽頁數(shù) (Pages / Session)
Pages / Session是指在單個會話內(nèi)頁面的瀏覽數(shù),這個指標 提升了35.54 掏觉。SPA改善了頁面間跳轉(zhuǎn)的速度区端,獲取了顯著的效果。
跳出率(Bounce Rate)
跳出率指在一個會話內(nèi)澳腹,僅看了一個頁面的比率织盼,這個指標 改善了44.44% 杨何。我們認為這是由于首屏和頁面跳轉(zhuǎn)速度的改善,用戶界面升級(更容易理解的分頁)沥邻,「咯噔」改進所帶來的結(jié)果危虱。
然而還存在很多改進的余地,任何一個指標都可以再次提升唐全。我們想以此表明 網(wǎng)站性能的提升會帶來業(yè)務指標的提升?? 埃跷。
上述數(shù)據(jù)是在以下條件下取得的:
- 頁面性能
- 使用 SpeedCurve
- 測試的URL是 http://s.ameblo.jp/ebizo-ichikawa/entry-12152370365.html
- 瀏覽器指定為 Chrome, 53.0.2785.143移動端模擬模式
- 網(wǎng)絡指定為4G模擬模式(14.6 Mbps,Upload 7.8Mbps邮利,Latency 53ms)
- 業(yè)務指標
- 使用 Google Analytics
- 獲取自 s.ameblo.jp 內(nèi)的全部數(shù)據(jù)
- 對2016年8月和2016年9月的數(shù)值進行比較
寫在最后
這次系統(tǒng)重構(gòu)的出發(fā)點是對技術(shù)的挑戰(zhàn)弥雹,結(jié)果獲得了良好的用戶反饋,并對業(yè)務作出了貢獻延届,我們自身也感到非常有價值剪勿,獲得了極大的成就感。采用最新迎合時代潮流的技術(shù)自然提升服務的質(zhì)量方庭,也使得這種文化在公司在生根厕吉。在此,對及早導入Isomorphic JavaScript二鳄,并向日本國內(nèi)推廣的同事 @ahomu 表示感謝赴涵!?? ??
作者介紹:
作者:原 一成(Hara Kazunari)媒怯,2008年加入日本CyberAgent公司订讼。擔任Ameblo 2016移動前端改版項目總負責人。著有《GitHubの教科書》扇苞,《CSS3逆引きデザインレシピ》欺殿,《フロントエンドエンジニア育成読本》。
譯者:侯 斌(Hou Bin)鳖敷,2014年入職日本CyberAgent公司〔彼眨現(xiàn)任Ameblo前端開發(fā)。在本次Ameblo 2016移動前端改版項目中擔任主要開發(fā)定踱,負責基礎(chǔ)架構(gòu)和技術(shù)選型以及關(guān)鍵模塊開發(fā)等棍潘。