アメブロ2016 アメブロフロント刷新にみる ひかりとつらみ
TRANSCRIPT
アメブロフロント刷新にみる ひかりとつらみ
2016/12/5 Frontrend Hara Kazunari @CyberAgent
アメブロ2016
🎉
@herablog
@kouhin
https://developers.cyberagent.co.jp/blog/archives/636/
😂YES
アメブロフロント刷新にみる ひかりとつらみ
アメブロ2016
PV DOWN
TOO SLOW TO LOAD
2016年4月のデータ
バックエンドのAPI化 http://www.slideshare.net/nin2hanzo/spring-69237035
! "#表示速度
改善システム モダン化
UX ver. 2016
GOAL
#表示速度は、Web UXにとって、最も大事な項目のひとつです。
モダンなエコシステムを使うことは、良いアプリを作るのに役立ちます。
!
時代にあった、ユーザー体験を作ることで使いやすいアプリになります。
"
! "#表示速度
改善システム モダン化
UX ver. 2016
GOAL
コンセプト決め
by Steve Souders
🙏
by Steve Souders
by Steve Souders
サーバーサイドははやい HTMLのサイズが大きめ
ブロッキングリソース多め リソース多いサイズも大きい
💡
サーバーサイドははやいまま HTMLのサイズ減らす
リソースの非同期読み込み ATF以外のリソースの遅延表示
💡
ブログというプロダクト
テキスト中心の読み物 SEO大事 一度に複数ページ 見られている
SSR SPA
Before
After
SPASSR
SSR SSR SSR
SPA
SSR
First Paint SEO
Runtime Paint UX
Before After
LAZY LOAD
Before
Main
Sub
Not displayed
Above The Fold
After
Main
Sub
Not displayed
HTML Size -20%16.1 → 13.5 KB$
#Under 100msHTML Response Time
renderToString()8,000 rps at Ameba
300ms - 500ms, High CPU usage
Caching HTML/API Data Dynamic CSS Classes
node
API
Cache
ClientHTTP
Blog Data
Blog Data
zlib
HTML
Add client info to <body>
as class name
HTML ResponsesEntry List Pages
Entry Pages
2016年9月のデータ
! "#表示速度
改善システム モダン化
UX ver. 2016
GOAL
まるでexampleアプリのように
React with Redux
コンポーネント志向 Pure functions
React with Redux
Page
Navi Article Paging
コンポーネント志向
React with Redux
Action
Page
Navi Article Paging
Reducer
props
State (Store)
propsobject
object
func
Pure functionsによる一定フロー
React with Redux
State State State
Stateの情報だけで表示内容が確定できる
CSS Modules各コンポーネントごとのスタイル
SpNavigationBar.js SpNavigationBar.css
.Nav { background: #fff; border-bottom: 1px solid #e3e5e4; display: flex; height: 40px; width: 100%; }
.Logo { text-align: center; }
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> ); } }
css-loader
CSS Modules各コンポーネントごとのスタイル
.SpNavigationBar__Logo___${HASH}(BEM CamelCase style: MyBlock__SomeElem_modName_modVal)
https://en.bem.info/methodology/naming-convention/#camelcase-style
CSS Modules各コンポーネントごとのスタイル
スコープがきれる 影響範囲が絞れる
モジュール・開発者多い アメブロでは機能
Atomic Design
Presentational and Container Components
% &Containers Components
状態を持つコンポーネント 表示内容だけのコンポーネント
Atomic Design
% &Containers Components
状態を持つコンポーネント 表示内容だけのコンポーネント状態が肥大化😿 処理が肥大化😿
どっちに書いていいかわからない🙀
Atomic Design
最小単位
<Icon>
状態持つ
<Entry>
再利用
<List>
各ページ SPA用
organisms/SpEntry.js
organisms/SpBlogInfo.js
organisms/SpNavigationBar.js
atoms/Icon.js
molecules/BloggerThumbnail.js
organisms/SpEntry.js
organisms/SpBlogInfo.js
organisms/SpNavigationBar.js
atoms/Icon.js
molecules/BloggerThumbnail.js
Atomic DesignOrganismのコンポーネントは状態を持てる
connectを使って状態を持てる データ取得処理を記述できる
import React from 'react'; import { connect } from 'react-redux'; import { routerHooks } from 'react-router-hook'; import { fetchBloggerRequest } from '../../../actions/bloggerAction';
// データ取得処理 (react-router-hookを利用) const defer = async ({ dispatch }) => { await dispatch(fetchBloggerRequest()); };
// Redu storeのstateをpropsとして利用 const mapStateToProps = (state, owndProps) => { const amebaId = owndProps.params.amebaId; const blogger = state.bloggerMap.bloggerMap[amebaId].nickName; return { nickName, }; };
@connect(mapStateToProps) @routerHooks({ done }) export class SpProfileInfo extends React.Component { static propTypes = { nickName: React.PropTypes.string.isRequired, };
render() {
SSR SPA
Isomorphic JavaScript
Isomorphic JavaScript
ほとんどJavaScript
Isomorphic JavaScript
Action
Page
Navi Article Paging
Reducer
State (Store)
% actions/
% components/
% reducers/
% services/
& server.js
& client.js ブラウザの入り口
サーバーの入り口
node API
Babel
決まっている未来は、 早めに試そう
Babelexport function getPages(amebaId, page = 1) { const url = `https://api.jp/pages/${amebaId}/${page}`; return fetch(url); }
publicBlogger.getPages(amebaId) .then((res) => res.json()) .then(json => json.data);
fetch, Template Strings, default arguments, arrow functions, Promise
Babelexport default class SpThumbnail extends React.Component { render() { const { id, ...restProps } = this.props; return ( <Thumbnail
{...restProps} src=`https://img.com/${id}`
/> ); } }
Rest Parameters
Babelconst defer = async ({ dispatch, getState, params }) => { const amebaId = params.amebaId; const blogger = await dispatch(fetchBloggerRequest(amebaId)); if (blogger.isOfficial) {} };
@routerHooks({ defer }) export class SpBlogHeaderInfo extends React.Component { }
async/await, class, import/export, decorator
Babel
最悪、該当部分だけ書き直すのは難しくない …はず😅
ESLint, Stylelint
比較的固めのルールを選択 ガイドラインを明確に
eslint-config-airbnb, stylelint-config-standard
ESLint, Stylelint
例えばこんなのもエラー
ESLint, Stylelintconst foo = { a: ‘a’, b: ‘b’ // comma-dangle } // semi
.Block{ // block-opening-brace-space-before border-top: 1px solid #CCC; // color-hex-case
border-bottom: 1px solid #ccc; // declaration-block-properties-order color:#333; // declaration-colon-space-after } // no-missing-end-of-source-newline
ESLint, Stylelint
個人的な好みはあるが… ルールを統一して一貫性を保つ
レビュー時に毎回指摘は気まずい…😇
CIPushされたものは 必ずCIテスト を通して 安全性を担保する
CI
Docker
node.jsにポータビリティをもたらす
Docker
一度、イメージを作ってしまえば、 複数サーバーへのリリース
切り戻しも簡単
Docker
さらに、nodeのアップデートも 簡単
Docker
FROM node:7.2
Dockerfileを変えるだけ
Docker
アメブロでは 極力node最新版を利用する
CI, Lint Semantic Versioning
README.md CONTRIBUTING.md
PULL_REQUEST_TEMPLATE.md etc.
OSSっぽく開発
外でもそのまま使える技術を
! "#表示速度
改善システム モダン化
UX ver. 2016
GOAL
No more ガタンッ🎯
ATFのコンテンツの高さを固定 誤タップはモバイルで
最悪のUXのひとつ
No more ガタンッ🎯
No more ガタンッ🎯
コンテンツファースト
Before After
アクセシビリディ
伊原 力也さん
太田 良典さん
アクセシビリディ
https://speakerdeck.com/herablog/ameburowosukurinridadedu-mishang-getemita-2016nian-xia
! "#表示速度
改善システム モダン化
UX ver. 2016
GOAL
#Business & System✌
GOOD BOTH
'
Desktop version Markup, a11y
Design x Development Webpack2
https, HTTP/2 Service Worker, PWA, etc.
Roadmap
https://developers.cyberagent.co.jp/blog/@ca_developers
@herablog
アメブロフロント刷新にみる ひかりとつらみ
アメブロ2016
Reduxむずい😂
新メンバーが理解するまで 最大2ヶ月かかる (当社比)
(実装しながらわかってくる)
Lintスパルタ😂
最初はだいたいCIエラー
アメブロモジュール大杉😂
文章ママ
周辺技術も大杉😂
結論
みんなで、頑張ろ😂
🍣🍕🍺🍣🍕🍺🍣🍕
🍺🍣🍕🍺🍣🍕🍺🍣
🍕🍺🍣🍕🍺🍣🍕🍺
🍺🍕🍺🍣🍕🍺🍣🍕
🍕🍺🍕🍺🍣🍕🍺🍣