Ameblo2016 ~ React/Redux打造的同構(gòu)Web應(yīng)用(譯)

譯自 CyberAgent Developers Blog
原文標(biāo)題 「アメブロ2016 ~ React/ReduxでつくるIsomorphic web app ~」
作者 HERABLOG

大家好,我是原一成(@herablog),目前在CyberAgent主要擔(dān)任前端開發(fā)。

Ameblo(注: Ameba博客,Ameba Blog,簡稱Ameblo)于2016年9月,將前端部分由原來的Java架構(gòu)的應(yīng)用,重構(gòu)成為以node.js、React為基礎(chǔ)的Web應(yīng)用。這篇文章介紹了本次重構(gòu)的起因、目標(biāo)、系統(tǒng)設(shè)計以及最終達(dá)成的結(jié)果。

新系統(tǒng)發(fā)布后,立即就有人注意到了這個變化。

twitter_msg.png

系統(tǒng)重構(gòu)的起因

2004年起,Ameblo成為了日本國內(nèi)最大規(guī)模的博客服務(wù)。然而隨著系統(tǒng)規(guī)模的增長,以及很多相關(guān)人員不斷追加各種模塊、頁面引導(dǎo)鏈接等,最終使得頁面展現(xiàn)緩慢、對網(wǎng)頁瀏覽量(PV)造成了非常嚴(yán)重的影響。并且頁面展現(xiàn)速度方面,絕大多數(shù)是前端的問題,并非是后端的問題。

基于以上這些問題,我們決定以提高頁面展現(xiàn)速度為主要目標(biāo),對系統(tǒng)進(jìn)行徹底重構(gòu)。與此同時后端系統(tǒng)也在進(jìn)行重構(gòu),將以往的數(shù)據(jù)部分進(jìn)行API化改造。此時正是一個將All-in-one的巨型Java應(yīng)用進(jìn)行適當(dāng)分割的絕佳良機。

目標(biāo)

本次系統(tǒng)重構(gòu)確立了以下幾個目標(biāo)。

頁面展現(xiàn)速度的改善(總之越快越好)

用于測定用戶體驗的指標(biāo)有很多,我們認(rèn)為其中對用戶最重要的指標(biāo)就是頁面展現(xiàn)速度。頁面展現(xiàn)速度越快,目標(biāo)內(nèi)容就能越快到達(dá),讓任務(wù)在短時間內(nèi)完成。這次重構(gòu)的目標(biāo)是盡可能的保持博客文章、以及在Ameblo內(nèi)所呈現(xiàn)的繁多的內(nèi)容的固有形式,在不破壞現(xiàn)有價值、體驗的基礎(chǔ)上,提高展現(xiàn)和頁面行為的速度。

系統(tǒng)的現(xiàn)代化(搭乘生態(tài)系統(tǒng))

從前的Web應(yīng)用是將數(shù)據(jù)以HTML的形式返回,那個時候并沒有什么問題。然而,隨著內(nèi)容的增加,體驗的豐富化,以及設(shè)備的多樣化,使得前端所占的比重越來越大。此前要開發(fā)一個好的Web應(yīng)用,如果要高性能,就一定不要將前后端分隔開。當(dāng)年以這個要求開發(fā)的系統(tǒng),在經(jīng)歷了10年之后,已經(jīng)遠(yuǎn)遠(yuǎn)無法適應(yīng)當(dāng)前的生態(tài)系統(tǒng)。

「跟上當(dāng)前生態(tài)系統(tǒng)」,以此來構(gòu)建系統(tǒng)會帶來許許多多的好處。因為作為核心的生態(tài)系統(tǒng),其開發(fā)非?;钴S,每天都會有許許多多新的idea。因而最新的技術(shù)和功能更容易被吸納,同時實現(xiàn)高性能也更加容易。同時,這個「新」對于年輕的技術(shù)新人也尤為重要。僅懂得舊規(guī)格舊技術(shù)的大叔對于一個優(yōu)秀的團(tuán)隊來說是沒有未來的(自覺本人膝蓋也中了一箭)。

升級界面設(shè)計、用戶體驗(2016年版Ameblo)

Ameblo的手機版在2010年經(jīng)歷了一次改版之后,就基本上沒有太大的變化。這其間很多用戶都已經(jīng)習(xí)慣了原生應(yīng)用的設(shè)計和體驗。這個項目也是為了不讓人覺得很土很難用,達(dá)到順應(yīng)時代的2016年版界面設(shè)計和用戶體驗。

OK,接下來讓我具體詳細(xì)聊聊。

頁面加載速度的改善

改善點

系統(tǒng)重構(gòu)前,通過 SpeedCurve 進(jìn)行分析,得出了下面結(jié)論:

  • 服務(wù)器響應(yīng)速度很快
  • HTML文檔較大(頁面所有要素都包含其中)
  • 阻塞頁面渲染的資源(JavaScript、Stylesheet)較多
  • 資源讀取的次數(shù)過多,體積過大

依據(jù)這些確定了下面這幾項基本方針:

  • 為了不致于降低服務(wù)器響應(yīng)速度,對代碼進(jìn)行優(yōu)化,緩存等
  • 盡可能減少HTML文檔大小
  • JavaScript異步地加載與執(zhí)行
  • 最初呈現(xiàn)頁面時,僅僅加載所需的必要資源

SSR還是SPA

近年來相比于添加到收藏夾中,用戶更傾向于通過搜索結(jié)果、Facebook、Twitter等社交媒體上的分享鏈接打開博客頁面。Google和Twitter的AMP, Facebook的Instant Article表明第一頁的展現(xiàn)速度極大影響到用戶滿意度。

此外,從Google Analytics等日志記錄中了解到在文章列表頁面和前后文章間進(jìn)行跳轉(zhuǎn)的用戶也很多。這或許是因為博客作為個人媒體,當(dāng)某一用戶看到一篇不錯的文章,非常感興趣的時候,他也同時想看一看同一博客內(nèi)的其它文章。也就是說,博客這種服務(wù) 第一頁快速加載與頁面間快速跳轉(zhuǎn)同等重要

因此,為了讓兩者都能發(fā)揮最佳性能,我們決定在第一頁使用服務(wù)器端渲染(Server-side Rendering, SSR),從第二頁起使用單頁面應(yīng)用(Single Page Application, SPA)。這樣一來,既能確保第一頁的展示速度和機器可讀性(Machine-Readability)(含SEO),又能獲得SPA帶來的快速展示速度。

BTW,對于目前的架構(gòu),由于服務(wù)器和客戶端使用相同的代碼,全部進(jìn)行SSR或是全部進(jìn)行SPA也是可能的。目前已經(jīng)實現(xiàn)即便在不能運行JavaScript的環(huán)境中,也可以正常通過SSR來瀏覽。可以預(yù)見將來等到Service Worker普及之后,初始頁面將更加高速化,而且可以實現(xiàn)離線瀏覽。

z-ssrspa.png

以前的系統(tǒng)完全使用SSR,而現(xiàn)在的系統(tǒng)從第二頁起變?yōu)镾PA。

z-spa-speed.gif

SPA的魅力在于呈現(xiàn)速度之快。因為僅僅通過API獲取所需的必要數(shù)據(jù),所以速度非???!

延遲加載

我們使用SSR+SPA的方法來優(yōu)化頁面間跳轉(zhuǎn)這種橫向移動的速度,并且使用延遲加載來改善頁面的縱向移動速度。一開始要展現(xiàn)的內(nèi)容以及導(dǎo)航,還有博客文章等最早呈現(xiàn),在這些內(nèi)容之下的次要內(nèi)容隨著頁面的滾動逐漸呈現(xiàn)。這樣一來,重要的內(nèi)容不會受頁面下面內(nèi)容的影響而更快的顯示出來。對于那些想盡快讀文章的用戶來說,既不增加用戶體驗上的壓力,又能完整的提供頁面下方的內(nèi)容。

z-lazyload.png

之前的系統(tǒng)因為將頁面內(nèi)的全部內(nèi)容都放到HTML文檔里,所以使得HTML文檔體積很大。而現(xiàn)在的系統(tǒng),僅僅將主要內(nèi)容放到HTML里返回,減少了HTML的體積和數(shù)據(jù)請求的大小。

HTML緩存

博客文章是靜態(tài)文檔,對于特定URL的請求會返回固定的內(nèi)容,因此非常適合進(jìn)行緩存。緩存使得服務(wù)器處理內(nèi)容減少,在提高頁面響應(yīng)速度的同時減輕了服務(wù)器的負(fù)擔(dān)。我們將不變的內(nèi)容(文章等)生成的HTML進(jìn)行緩存返回,對于由于變化的內(nèi)容能過JavaScript、CSS等進(jìn)行操作(比如顯示、隱藏等)。

z-newrelic-entrylist.png

這張圖顯示了2016年9月最后一周New relic上的統(tǒng)計數(shù)據(jù)。文章列表頁面的HTML的響應(yīng)時間基本在50ms以下。

z-newrelic-entry.png

這張圖是文章詳細(xì)頁面的統(tǒng)計數(shù)據(jù)??梢钥闯?,這個頁面的響應(yīng)時間也基本上是在50ms以下。由于存在文章過長的時候會造成頁面體積變大,以及文章頁面不能完全緩存等情況,所以相比列表頁面會存在更多較慢的響應(yīng)。

對于因請求的客戶端而產(chǎn)生變化部分的處理,我們在HTML的body標(biāo)簽中通過加入相應(yīng)的class,然后在客戶端通過JavaScript和CSS等進(jìn)行操作。比如,一些內(nèi)容不想在某些操作系統(tǒng)上顯示,我們就用CSS對這些內(nèi)容進(jìn)行隱藏。由于CSS樣式表會先載入,頁面布局確定下來之后再進(jìn)行頁面渲染,所以這個也可以解決后面要提到的「咯噔」問題。

<!-- html -->

<body class="OsAndroid">
/* main.css */

body.OsAndroid .BannerForIos {
  dsplay: none;
}

系統(tǒng)的現(xiàn)代化(搭乘生態(tài)系統(tǒng))

技術(shù)選型

這次項目的技術(shù)選擇時,遵循了盡可能采用當(dāng)前當(dāng)前市場上已經(jīng)存在的普遍使用的技術(shù)這一原則。暗號就是:「活脫脫像范例應(yīng)用一樣Start」。這樣一來,無論是誰都可以輕松的獲取到相應(yīng)的文檔等信息,同時其它的團(tuán)隊和公司如果要參與到項目中來也能很快的上手。然而在真正進(jìn)行開發(fā)的時候,一些細(xì)節(jié)實現(xiàn)上因為各種各樣的原因存在一些例外的情況,但是在極大程度上保持了各個模塊的獨立性。最終系統(tǒng)的大體構(gòu)成如下圖所示:

z-bigpicture.png

(有些地方做了省略)

React with Redux

使用React和React進(jìn)行開發(fā)的的時候,很多地方可以用 純函數(shù) 的形式進(jìn)行組合。純函數(shù)是指特定的參數(shù)總是返回特定的結(jié)果,不會對函數(shù)以外的范圍造成污染。使用純函數(shù)進(jìn)行開發(fā)可以保證各個處理模塊最小化,不用擔(dān)心會無意間改變引用對象的值。這樣一來,十分有助于大規(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進(jìn)行復(fù)制并更新的函數(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ìn)行更新。各個組件依據(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的信息在 官方文檔 中說明得非常詳細(xì),推薦隨時參考一下這個文檔。

同構(gòu)Web應(yīng)用(Isomorphic web app)

Ameblo 2016年版基本上完全是用JavaScript重寫的。無論是Node服務(wù)器上還是客戶端上都使用了相同的代碼和流程,也就是所謂的同構(gòu)Web應(yīng)用。項目的目錄結(jié)構(gòu)大體上如下所示,服務(wù)器端的入口文件是 server.js ,瀏覽器的入口文件是 client.js

  • actions/ Redux Action (服務(wù)器,客戶端共用)
  • api/ 封裝的API接口
  • components/ React組件 (服務(wù)器,客戶端共用)
  • reducer/ <span class="underline">Redux Reducers</span> (服務(wù)器,客戶端共用)
  • services/ 服務(wù)層模型,使用 Fetchr 對數(shù)據(jù)請求進(jìn)行適當(dāng)粒度的劃分。同時這個也使得node.js作為代理,間接請求API(服務(wù)器專用)。
  • server.js 服務(wù)器入口(服務(wù)器專用)
  • app.js node服務(wù)器的配置、啟動,由server.js調(diào)用(服務(wù)器專用)
  • client.js 客戶端入口(客戶端專用)
z-isomorphic.png

寫好的JavaScript同時運行在服務(wù)器端還是客戶端上的運行行為、以及從數(shù)據(jù)讀取直到在頁面上顯示為止的整個瀏程,都以相同的形式進(jìn)行。

z-code-stats.png

使用Github的語言統(tǒng)計可以看出 ,JavaScript占了整個項目的94.0%,幾乎全部都是由JavaScript寫成的。

原子設(shè)計(Atomic Design)

對于組件的規(guī)劃,我們采用了 原子設(shè)計 理念。其實項目并沒有一開始就采用原子設(shè)計,而是根據(jù) Presentational and Container Components ,對 containercomponent 進(jìn)行了兩層劃分。然而Ameblo中的組件實在是太多,很容易造成職責(zé)不明確的情況,因此最終采用了原子設(shè)計理念。項目的實際運用中,采用了以下的規(guī)則。

z-atomic-design.png

Atoms

組件的最小單位,比如Icon、Button等。原則上不具有狀態(tài),從父組件中獲取傳遞過來的props,并返回HTML。

Molecules

以復(fù)用為前提的組件,比如List、Modal、User thunmbnail等。原則上不具有狀態(tài),從父組件中獲取傳遞過來的props,并返回HTML。

Organisms

頁面上較大的一塊組件,比如Header,Entry,Navi等。對于這一層的組件,可以在其中進(jìn)行數(shù)據(jù)獲取處理,以及使用Redux State 和 connect ,維護(hù)組件的狀態(tài)。這里獲取的組件狀態(tài)以props的形式,傳遞給 MoleculesAtom 。

// 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)所對應(yīng)的組件。其職責(zé)是將所需的部件從Organisms中import過來,以一定的順序和格式整合在一起。

Pages

作為頁面的頁面組件?;旧鲜前褌鬟f過來的 this.props.children 原原本本的顯示出來。由于Ameblo是單頁面應(yīng)用,因而只有一個頁面組件。

CSS Modules

CSS樣式表使用 CSS Modules 將CSS樣式規(guī)則的作用范圍嚴(yán)格限制到了各個組件內(nèi)。各個樣式規(guī)則的作用范圍進(jìn)行限制使得樣式的變更和刪除更加容易。因為Ameblo是由許多人協(xié)同開發(fā)完成,不一定每個人都精通CSS,而且不免要時常對一些不知是誰何時寫的代碼進(jìn)行更改,在這個時候?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)一代碼風(fēng)格,節(jié)約代碼審查時的麻煩。具體規(guī)則分別繼承自 eslint-config-airbnbstylelint-config-standard ,對于一些必要的細(xì)節(jié)做了少許定制。因為規(guī)則較嚴(yán),起初的時候或許有點不便。新成員加入項目組時,代碼通過Lint測試便成了要通過的第一關(guān)??。

z-code-review.png

防止了代碼審查時對于這些細(xì)微寫法挑錯。被機器告知錯誤時,心理上會感覺稍好一些。

z-ci-error.png

加入項目組之后,最初的這段時間里發(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.ymlpackage.json (node環(huán)境下)里。

  • develop 開發(fā)(下次發(fā)布)用分支。構(gòu)建、測試之后自動部署到staging環(huán)境中。
  • release/vX.X.X 發(fā)布分支。由develop分支派生,構(gòu)建、測試之后,自動部署到semi(準(zhǔn)生產(chǎn))環(huán)境中。
  • hotfix/vX.X.X hotfix分支。由master分支派生,構(gòu)建、測試之后,自動部署到semi(準(zhǔn)生產(chǎn))環(huán)境中。
  • deploy/${SERVER_NAME} 部署到分支所指定的相應(yīng)服務(wù)器上。主要是在開發(fā)環(huán)境中使用。
  • master 這個分支構(gòu)建之后生成可以用于部署到production(生產(chǎn))環(huán)境的docker鏡像。
  • 其它 開發(fā)用分支。僅進(jìn)行構(gòu)建和測試。

Docker

本次系統(tǒng)重構(gòu),也對node.js應(yīng)用進(jìn)行docker化構(gòu)建。這次重構(gòu)的是前端系統(tǒng),我們希望可以在細(xì)小修正之后立即進(jìn)行部署。docker化之后,一旦將鏡像構(gòu)建完成,可以不受node模塊版本的左右進(jìn)行部署,回滾也很容易。

此外,node.js本身發(fā)布非常頻繁,如果放置不管,不知不覺之間系統(tǒng)就成古董了。docker化之后,可以不受各主機環(huán)境的影響自由的進(jìn)行升級。

更重要的是,設(shè)置docker容器數(shù)是比較容易的,這對于系統(tǒng)橫向擴容以及對服務(wù)器配置作優(yōu)化時也十分方便。

升級界面設(shè)計、用戶體驗(2016年版Ameblo)

不再「咯噔」

系統(tǒng)重構(gòu)之前的Ameblo由于存在一些高度沒有固定的模塊,出現(xiàn)了「咯噔」現(xiàn)象。這種「咯噔」會導(dǎo)致誤點擊以及頁面的重繪,十分令人厭煩。而此模塊高度固定也做為本次系統(tǒng)重構(gòu)的UI設(shè)計的前提。特別是頁面間導(dǎo)航作為十分重要的元素,我們經(jīng)過努力使得在頁面跳轉(zhuǎn)時每次都可以觸擊到相同的位置。

z-gatan.gif

「咯噔」的一個例子。點擊[次のページ](下一頁)的時候,額外的元素由于加載緩慢,造成誤點擊。

z-paging-fixed.gif

系統(tǒng)重構(gòu)之后,元素的位置被固定下來,減輕了頁面跳轉(zhuǎn)時給用戶心理上帶來的負(fù)擔(dān)。

智能手機時代的用戶界面

2016年在移動環(huán)境下使用的用戶幾乎都在使用智能手機。在智能手機上,由于各個平臺的提供者制定了各自不同的用戶界面規(guī)范,用戶已經(jīng)習(xí)慣并適應(yīng)了用戶界面。相比之下,雖說瀏覽器上的規(guī)范非常少,但是如果和當(dāng)今流行的界面差距太大的話,就會變得很難用。

Ameblo的手機版在2010年進(jìn)行改版之后,自然對一些細(xì)節(jié)進(jìn)行了改善,但是由于沒有太大的變動,所以現(xiàn)在看來很多地方已經(jīng)給人一種很舊的印象。用戶在瀏覽的時候,對于界面并不區(qū)別是原生應(yīng)用還是瀏覽器,因而制作出適應(yīng)當(dāng)前時代這個平臺的用戶界面顯得尤為重要。這里介紹一下本次重構(gòu)中,對于界面的一些升級。

z-update-design.png

內(nèi)容占據(jù)界面上橫向整個空間。2010年的時候,一般采用Twitter倡導(dǎo)的「將各個模塊圈起來的設(shè)計」。

z-searchbar.gif

增加了導(dǎo)航欄,把導(dǎo)航相關(guān)操作集中放置在這里。

可訪問性

這次系統(tǒng)重構(gòu)正值可訪問性成為熱點話題的時候。仔細(xì)的為HTML增加相當(dāng)標(biāo)簽屬生就可以使整個系統(tǒng)足夠可訪問。首先在HTML標(biāo)簽屬性添加上時要用心斟酌。對于標(biāo)題、 img 等添加適當(dāng)?shù)?alt 屬性,對于可點擊的元素一定要使用 a button 等可點擊的標(biāo)簽。如果能自動對可訪問性進(jìn)行檢驗就再好不過了,ESlint的 jsx-a11y 插件可以幫助完成這一點。

在項目進(jìn)行的時候,正好公司內(nèi)開展了一次可訪問性的學(xué)習(xí)活動( Designing Web Accessibility 的作者太田先生和伊原先生也參加了這次活動),在這次活動上也嘗試了Ameblo到目前為止沒有注意過的語音朗讀器。當(dāng)時用語音朗讀器在Ameblo上進(jìn)行朗讀時,有幾處有問題的地方,使用 WAI-ARIA 對這幾處加以修正(與 data-* 相同,JSX也支持 aria-* 屬性)。

這里 的PPT中有詳細(xì)的介紹,歡迎閱覽(日文)。

結(jié)果

OK,上面介紹了本次重構(gòu)帶來的很多變化,那么結(jié)果如何呢?

首先是性能相關(guān)指標(biāo)(測試的URL都是Ameblo中單一頁面請求資源最多,展示速度最慢的頁面)。

阻塞渲染的資源(Critical Blocking Resources)

z-speed-blocking.png

阻塞渲染的資源數(shù) 減少了75% !JavaScript全部變成了異步讀取與執(zhí)行。CSS樣式因為運營的原因,維持了重構(gòu)前的狀態(tài)。

內(nèi)容請求(Content Requests)

z-speed-requests.png

資源請求數(shù) 減少了58.04% !由于使用了延遲加載,首屏顯示只加載必要的資源,與此同時對文件進(jìn)行適當(dāng)?shù)恼恚h除了一些不必要的模塊,最終達(dá)成了這個狀態(tài)。

渲染(Rendering)

z-speed-rendering.png

渲染速度做為前端的主要性能指標(biāo),本次 提升了44.68% 。

頁面加載時間(Page Load Time)

z-speed-pageload.png

頁面加載時間 縮短了40.5 !此外,后端的返回時間也維持在了0.2ms ~ 0.3ms之間。

接下來介紹一下相關(guān)的業(yè)務(wù)指標(biāo)。

網(wǎng)頁瀏覽量(Pageviews)

z-ga-pv.png

因為2016年9月有一位有名的博客主成為了熱點話題,所以這個指標(biāo)內(nèi)含有特殊情況。網(wǎng)頁瀏覽量提升了57.15%。如果將熱點話題所帶來的數(shù)值除去后,實際上單純由系統(tǒng)重構(gòu)所帶來的提升在10%到20%之間。

每次會話瀏覽頁數(shù) (Pages / Session)

z-ga-pps.png

Pages / Session是指在單個會話內(nèi)頁面的瀏覽數(shù),這個指標(biāo) 提升了35.54 。SPA改善了頁面間跳轉(zhuǎn)的速度,獲取了顯著的效果。

跳出率(Bounce Rate)

z-ga-bounce.png

跳出率指在一個會話內(nèi),僅看了一個頁面的比率,這個指標(biāo) 改善了44.44% 。我們認(rèn)為這是由于首屏和頁面跳轉(zhuǎn)速度的改善,用戶界面升級(更容易理解的分頁),「咯噔」改進(jìn)所帶來的結(jié)果。

然而還存在很多改進(jìn)的余地,任何一個指標(biāo)都可以再次提升。我們想以此表明 網(wǎng)站性能的提升會帶來業(yè)務(wù)指標(biāo)的提升?? 。

上述數(shù)據(jù)是在以下條件下取得的:

  • 頁面性能
  • 業(yè)務(wù)指標(biāo)
    • 使用 Google Analytics
    • 獲取自 s.ameblo.jp 內(nèi)的全部數(shù)據(jù)
    • 對2016年8月和2016年9月的數(shù)值進(jìn)行比較

寫在最后

這次系統(tǒng)重構(gòu)的出發(fā)點是對技術(shù)的挑戰(zhàn),結(jié)果獲得了良好的用戶反饋,并對業(yè)務(wù)作出了貢獻(xiàn),我們自身也感到非常有價值,獲得了極大的成就感。采用最新迎合時代潮流的技術(shù)自然提升服務(wù)的質(zhì)量,也使得這種文化在公司在生根。在此,對及早導(dǎo)入Isomorphic JavaScript,并向日本國內(nèi)推廣的同事 @ahomu 表示感謝!?? ??

作者介紹:

作者:原 一成(Hara Kazunari),2008年加入日本CyberAgent公司。擔(dān)任Ameblo 2016移動前端改版項目總負(fù)責(zé)人。著有《GitHubの教科書》,《CSS3逆引きデザインレシピ》,《フロントエンドエンジニア育成読本》。

譯者:侯 斌(Hou Bin),2014年入職日本CyberAgent公司?,F(xiàn)任Ameblo前端開發(fā)。在本次Ameblo 2016移動前端改版項目中擔(dān)任主要開發(fā),負(fù)責(zé)基礎(chǔ)架構(gòu)和技術(shù)選型以及關(guān)鍵模塊開發(fā)等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容