$30 off During Our Annual Pro Sale. View Details »

Rails + ReactなSPAサイトでのSEO / SEO at Rails + React SPA

adorechic
September 29, 2018

Rails + ReactなSPAサイトでのSEO / SEO at Rails + React SPA

概要
ReactやVue.jsを用いたSPAサイトが増え、SEOを目的としたSSRやパフォーマンスチューニングの事例も増えてきています。
一方で近年のフロントエンド技術の裾野は広く、同じことをやろうとしても技術スタックが異なる場合同じ手法が使えないということがままあります。
例えばReact-RailsはSSR機能を備えており、オプションで指定するだけでSSRが可能です。
しかし非同期フローとしてRedux-Sagaを採用していると、二回のrenderToStringが必要となり、かつ二回目はJSのコールバックで実行されるためそのままではReact-RailsでSSRすることができません。
もし仮にRailsでなくNode.jsを利用していればこのコールバックを自然に扱うすることができるため問題になりません。

最終的な構成の事例だけを見ていると、なぜそういった構成になっているかの経緯まで掴みきれず、いざ自分でやってみると躓くポイントは随所にあります。

そこで本セッションではRails + React + Redux + Redux-SagaなアプリケーションのSEO対策として、hypernovaによるSSRやパフォーマンス改善に取り組んだ事例について時系列でどういった背景によって構成を変えていったかを紹介します。

トピック
Universal JS
Redux-SagaをRailsでSSRする
React-Headをhypernova環境でSSRする
Code Splitting
SplitChunks(CommonsChunkPlugin) vs Dynamic Imports

adorechic

September 29, 2018
Tweet

More Decks by adorechic

Other Decks in Programming

Transcript

  1. 3BJMT3FBDUͳ
    41"αΠτͰͷ4&0

    View Slide

  2. ࣗݾ঺հ
    w٢઒ਸྙ JEBEPSFDIJD

    w Φ΢νʔϊऔక໾$50
    w όοΫΤϯυ԰
    w ࠃ಺େखϒϩάαʔϏε΍ϨγϐαʔϏεձࣾͷόοΫΤϯυ
    w ݩ.JDSPTFSWJDFT͓͡͞Μ
    w CVJMEFSTDPOͰΩʔϘʔυ೤͕ߴ·͍ͬͯΔ

    View Slide

  3. 41"4&0

    View Slide

  4. 41"4&0΍Δલ
    w443͠ͱ͚͹͍͍ΜͰ͠ΐ
    w 443ͪ͠Ό͑͹ඇ41"αΠτͱಉ͡Α͏ͳ΋ΜͰ͠ΐ
    w Ͱ΋࠷ۙ͸+4ධՁͯ͘͠ΕΔΒ͍͠͠ෆཁ͔΋
    w443ͷπʔϧ΋͍Ζ͍Ζ͋Γͦ͏
    w IZQFSOPWBͱ͔/FYUKTͱ͔Α͘ฉ͘ʢҧ͍͸Α͘Θ͔͍ͬͯͳ͍ʣ
    wଞࣾͷߏ੒ࣄྫ͸ͪΒ΄Βݟ͔͚Δ
    w ͦΕࢀߟʹಋೖͰ͖ͦ͏

    View Slide

  5. ݱࡏͷߏ੒
    w3FBDU
    w 3FEVY 4BHB 3FBDU3PVUFS 3FBDU)FBE
    w %ZOBNJD*NQPSUͰ$PEF4QMJUUJOH
    w XFCQBDLFS
    w $43༻ͱ443༻ͰXFCQBDLDPOpH͸݁ߏΧελϜ͍ͯ͠Δ
    w3BJMTIZQFSOPWB

    View Slide

  6. ͳΜ͔Ͳ͔͜Ͱݟͨ͜ͱ͋Δߏ੒ʁ

    View Slide

  7. 41"4&0΍ͬͯΔ࠷த
    w443͠ͱ͚͹͍͍ͳΜͯ୭͕ݴͬͨΜͩʂʂʂ
    w ؾΛ͚ͭΔ͜ͱ͕ඇ41"ͱ݁ߏҧ͏
    w ͦ΋ͦ΋443͕݁ߏ͍ͨ΁Μ
    wߏ੒͕Ұͭҧ͚ͬͨͩͰམͱ͕݀͠େྔʹ͋Δ
    w νϡʔτϦΞϧΛಈ͔͢ͷ͸؆୯
    w ଞࣾࣄྫ͸ϗϯτʹࢀߟఔ౓
    w ར༻͍ͯ͠Δ֤πʔϧΛͪΌΜͱཧղ͢Δඞཁ͕͋Δ

    View Slide

  8. ࠷ऴߏ੒͚ͩ·ͱΊΔͱ
    ࣦഊͷྺ࢙͕఻ΘΒͳ͍ͷͰ
    ࣌ܥྻͰ͓఻͑͠·͢

    View Slide

  9. View Slide

  10. Φ΢νʔϊ
    wෆಈ࢈ʹؔ͢ΔαʔϏε
    w ෆಈ࢈ݕࡧϙʔλϧ
    w ϥΠϑελΠϧϝσΟΞ
    w ෆಈ࢈৘ใπʔϧ

    View Slide

  11. ܦҢ
    wαΠτϦχϡʔΞϧͰ3FBDU3BJMTʹͨ͠
    w ࠷ॳ͸OPJOEFYͰݶఆతͳެ։Λ͍ͯͨ͠
    wશମతʹ੾Γସ͑ΔલʹΠϯσοΫε͍ͤͯ͜͞͏
    w ࠷ॳͷΰʔϧ͸͋͘·ͰΠϯσοΫεͤ͞Δ͜ͱ
    w ͜ͷஈ֊ͰݕࡧॱҐΛ͋͛Α͏ͱ·Ͱ͸ࢥ͍ͬͯͳ͔ͬͨ
    w ద੾ͳλάΛઃఆ͢ΔͳͲͯ͠OPJOEFYΛ֎ͨ͠
    w +4ධՁ͞ΕΔΒ͍͠ͷͰ443ແ͠Ͱ༷ࢠΛݟΑ͏

    View Slide

  12. ΠϯσοΫε͞Εͳ͍
    wݕࡧॱҐӠʑͰ͸ͳͦ͘΋ͦ΋ΠϯσοΫε
    ͯ͘͠Εͳ͍
    w αΠτϚοϓૹ৴ͳͲ͸࣮ࢪ
    w ͙Β͍͔͠ΠϯσοΫε͞Εͳ͍
    w λά͕ؒҧ͍ͬͯΔͱ͔͸ͳͦ͞͏
    w ΍ͬͺΓ443͠ͳ͍ͱ͍͚ͳ͍ͷ͔ɾɾɾʁ

    View Slide

  13. ͦ΋ͦ΋443ͱ͸
    wαʔόʔαΠυϨϯμϦϯά
    w αʔόʔଆͰ༧Ί+4Λ࣮ߦͯ͠%0.Λߏஙͯ͠
    ͔ΒϨεϙϯεΛฦ͢

    View Slide

  14. ΫϥΠΞϯταΠυϨϯμϦϯά
    αʔόʔ ϒϥ΢β
    EJWEJW EJW
    UBCMFDMBTTGPP

    UBCMF
    EJW
    'PP5BCMF
    IUNM
    +4
    ΄΅ۭͷIUNMΛฦ͢ ΫϥΠΞϯτଆͰ3FBDU
    SFOEFSͯ͠
    IUNMΛߏங͢Δ

    View Slide

  15. αʔόʔαΠυϨϯμϦϯά
    αʔόʔ
    ϒϥ΢β
    EJW
    UBCMFDMBTTGPP

    UBCMF
    EJW
    αʔόʔଆͰ
    3FBDUSFOEFSͯ͠
    IUNMΛ׬੒͔ͤͯ͞Β
    Ϩεϙϯε

    View Slide

  16. 3BJMTͰ443͢Δ৔߹
    wSFBDUSBJMT
    w 3BJMT্Ͱ&YFD+4ͰϨϯμϦϯά
    wIZQFSOPWB
    w 3BJMTͱ͸ผͷIZQFSOPWBαʔόʔʢ/PEFKT&YQSFTTʣͰϨϯμϦϯ
    άͯ͠3BJMT্ͰϚʔδ
    wSFBDU@PO@SBJMT
    w σϑΥϧτ͸&YFD+4͕ͩ1SP൛͸/PEF7.͕࢖͑ΔΒ͍͠

    View Slide

  17. &YFD+4
    w3VCZ͔Β+4Λ࣮ߦ͢Δ
    wϥϯλΠϜ͍Ζ͍Ζ
    w MJCWܥ UIFSVCZSBDFS NJOJ@SBDFS

    w /PEFKT
    w FUD
    wศར͕͍ͩΖ͍Ζ੍ݶ͸͋Δ
    w +4&WFOUMPPQʹ׬શʹ͸ରԠ͓ͯ͠ΒͣɺTFU5JNFPVUͱ͔࢖͑ͳ͍

    View Slide

  18. SFBDUSBJMT
    w3BJMT XFCQBDLFS
    Ͱ3FBDU࢖͏ͳΒͨͿΜ͔͜͜Β
    w SBJMTXFCQBDLFSJOTUBMMͰೖΔ
    w Φ΢νʔϊͰ΋࠷ॳ͜ΕΛ࢖͍ͬͯͨ
    w443͕؆୯
    w ΦϓγϣϯΛQSFSFOEFSUSVFʹ͢Δ͚ͩʂ
    w ૣ଎༗ޮʹͯ͠Έͨ

    View Slide

  19. େྔͷΤϥʔͰશવಈ͔ͳ͍
    w&YFD+4ଆͰϒϥ΢βݻ༗ͷ΋ͷʹ৮Δͱଈࢮ
    w XJOEPX MPDBM4UPSBHF FUD
    w UZQFPGXJOEPXͳͲ੍ͯ͠ޚͯ͠΍Δඞཁ͕͋Δ
    w6OJWFSTBM+BWB4DSJQUԽ
    w ϒϥ΢βͰ͔͠ಈ͔ͳ͍࣮૷Λ͠ͳ͍Α͏मਖ਼
    w Ͱ΋ϝσΟΞΫΤϦͱ͔MPDBM4UPSBHF࢖͍͍ͨɾɾɾ

    View Slide

  20. ؀ڥґଘͷίʔυΛந৅Խ
    class DeviceDetector {
    constructor(userAgent) {
    // CSRではメディアクエリ、SSRではUAを使う
    if ((typeof window) === 'undefined') {
    this.detector = new UADetector(userAgent)
    } else {
    this.detector = new MediaDetector()
    }
    }
    }
    // メインコードでは環境を意識せずに使う
    deviceDetector = new DeviceDetector(this.props.user_agent)
    if (deviceDetector.isSmartPhone()) {
    ...
    }

    View Slide

  21. )5.-ฦͬͯ͘ΔΑ͏ʹͳͬͨ
    wҰ෦ͷίϯςϯπ͕දࣔ͞Εͳ͍
    w "1*ͰσʔλΛऔಘͯ͘͠Δ෦෼
    w Ͳ͏΍ΒSFEVYTBHB͕࣮ߦ͞Ε͍ͯͳ͍

    View Slide

  22. SFEVYTBHB
    w3FEVYͰඇಉظॲཧΛѻ͏NJEEMFXBSF
    class EstateComponent extends React.Component {
    handleClick() {
    // Reducerのときと同じようにdispatch
    dispatch({ type: 'REQUEST_FIND_ESTATE', params: { this.props.estateId } })
    }
    }
    function* estateSaga() {
    yield takeEvery('REQUEST_FIND_ESTATE', fetchEstate)
    }
    function* fetchEstate(action) {
    try {
    const estate = yield call(API.fetchEstate, action.params.estateId)
    // 結果をreducerにdispatch
    yield put({ type: 'SUCCESS_FIND_ESTATE', estate })
    } catch (e) {
    yield put({ type: 'FAILURE_FIND_ESTATE', message: e.message })
    }
    }

    View Slide

  23. $43ͰͷTBHB
    ϒϥ΢β
    3FBDU 4BHB
    4BHBTUBSU
    SFOEFS
    EJTQBUDI
    "1*GFUDI
    5BLFBDUJPO
    EJTQBUDI
    3FEVDFS
    6QEBUFDPNQPOFOU
    ֓೦ͱͯ͠͸
    ผʑͷεϨουͰ
    ಈ͘Πϝʔδ
    SFOEFS࣌
    ൃߦ͞ΕͨBDUJPO܈Λ
    TBHB͕रͬͯ
    EJTQBUDIͯ͠໭͢

    View Slide

  24. ͦͷ··443͢Δͱ
    &YFD+4
    3FBDU 4BHB
    4BHBTUBSU
    SFOEFS
    EJTQBUDI
    4BHB͕ಈ͘લʹSFOEFS
    ؔ਺͕ऴྃ͢Δ
    3BJMT
    4BHB͕ಈ͔ͳ͍ঢ়ଶͰ
    ͷIUNMΛฦͯ͠͠·͏

    View Slide

  25. ճSFOEFS͢Δ
    3FBDU 4BHB
    SFOEFS
    EJTQBUDI
    "1*GFUDI
    5BLFBDUJPO
    EJTQBUDI
    3FEVDFS
    &/%BDUJPO͸
    ࣮ߦதͷTBHBλεΫ͕
    ऴྃͨ͠ΒTBHB΋ऴྃ
    &/%EJTQBUDI
    4BHBTUPQ
    3FEVYTUPSF
    3FBDU
    SFOEFS
    3BJMT
    ॳճSFOEFSͨ͠Β
    &/%EJTQBUDI
    ̍ճ໨ͷ
    TUPSFΛ࢖ͬͯ
    ࠶౓SFOEFS

    View Slide

  26. TBHBͷ443ํ๏ʢެࣜʣ
    store.runSaga(rootSaga).toPromise().then(() => {
    // sagaが終了したらrenderして返すコールバック
    res.status(200).send(
    layout(
    renderToString(rootComp),
    JSON.stringify(store.getState())
    )
    )
    }).catch((e) => {
    res.status(500).send(e.message)
    })
    renderToString(rootComp) // 1回目のrender
    store.close() // END actionをdispatch

    View Slide

  27. ճSFOEFS๏ͷ໰୊
    wϨεϙϯεฦ͢ਓ͕1SPNJTFΛѻ͑Δ
    ඞཁ͕͋Δ
    w ެࣜαϯϓϧ͸/PEFKT
    w &YFD+4ˠ3BJMTͷ৔߹͸ίʔϧόοΫΛ଴ͯͳ͍

    View Slide

  28. )ZQFSOPWB
    w"JSCOC੡443πʔϧ
    3BJMT )ZQFSOPWB /PEFKT

    SFOEFS
    SFOEFS3FBDU
    .FSHF
    )ZQFSOPWB͔Βͷ
    )5.-Λ3BJMTςϯϓϨʔ
    τจࣈྻʹૠೖ
    1SPNJTFͰฦͤ͹
    ίʔϧόοΫͷ݁ՌΛ
    ฦͯ͘͠ΕΔ

    View Slide

  29. )ZQFSOPWBͰTBHB΋ಈ͘Α͏ʹͳ͕ͬͨɾɾɾ
    w·ͩ෦෼తʹදࣔ͞Εͳ͍
    w TBHBλεΫͷ݁ՌͰผͷTBHBλεΫ͕ಈ͘Օॴ
    w&/%BDUJPO͸ͦͷ࣌఺ͰͷλεΫͷ׬ྃΛ଴ͭ
    w &/%EJTQBUDIޙʹ৽͘͠ൃߦ͞ΕͨBDUJPO͸଴ͨͳ͍
    wͱΓ͋͑ͣ֘౰λεΫ͸Ұͭʹ·ͱΊͯճආ
    w ૄ݁߹ੑΣɾɾɾ

    View Slide

  30. ίϯςϯπ͸443Ͱ͖ΔΑ͏ʹͳͬͨʂ
    wUJUMFλά͕ग़ͳ͍ɾɾɾ

    View Slide

  31. 3FBDUIFBE
    +49Ͱهड़͓͚ͯ͠͹
    ·ͱΊͯIFBEΛ࡞ͬͯ
    ͘ΕΔ
    const App = () => (


    Title of page


    // ...


    )

    View Slide

  32. 3FBDUIFBEͷ443 BU/PEFKT

    const headTags = [];
    const app = renderToString(



    );
    res.send(`


    ${renderToString(headTags)}


    ${app}

    View Slide

  33. )ZQFSOPWB͸EJWλάͷத਎͚ͩΛฦ͢
    !!!
    %html
    %head
    %meta{:content => "text/html; charset=UTF-8"...
    ...
    %body
    ...
    = render_react_component('app', react_props)
    ϨΠΞ΢τ͸
    3BJMTଆʹ͋Δ
    IZQFSOPWBSVCZ͕
    IZQFSOPWBTFSWFS
    ͔Βฦ͖ͬͯͨIUNM
    ยͰஔ׵

    ...

    SFBDUIFBEͰ
    ผ్SFOEFSͨ͠΋ͷΛ
    Ͳ͏΍ͬͯ3BJMTଆʹ౉͢ʁ

    View Slide

  34. Ұॹʹฦͯ͠ஔ׵ʢྗٕʣ
    !!!
    %html
    %head
    %meta{:content => "text/html; charset=UTF-8"...
    ...
    %hypernova-head-tags/
    %body
    ...
    = render_react_component('app', react_props)
    SFBDUIFBEʹSFOEFSͤ͞
    ͨ΋ͷΛಠࣗλάͰҰॹ
    ʹฦ͢
    IZQFSOPWBSVCZ͕
    ஔ׵ͨ͠ޙʹ
    ϚʔΧʔλάΛஔ׵

    ...


    ...

    View Slide

  35. ͍͍ͩͨ443Ͱ͖ͨʂࣾ಺Ͱ৮ͬͯ΋Β͏
    wදࣔ͸Ͱ͖ͯΔ͚Ͳಈ͖͕͓͔͍͠
    w ݕࡧͨ͜͠ͱͳ͍৔ॴͷ݁Ռ͕ग़ͯ͘Δ

    View Slide

  36. άϩʔόϧม਺ʹͳͬͯͨ
    wΫϥε֎ͷม਺ͳͲ
    w $43Ͱ͔͠ಈ͍͍ͯͳ͔ͬͨͷͰ໰୊ͳ͔ͬͨ
    wҰͭҰͭ௚ͨ͠

    View Slide

  37. 443ϦϦʔεʂ

    View Slide

  38. ΠϯσοΫε
    ͞Εͳ͔ͬͨɾɾɾ

    View Slide

  39. ΋͔ͯ͠͠දࣔ଎౓ɾɾɾʁ
    wUFTUNZTJUF
    w (ճઢͰඵ͔͔Δͱ͍͏ධՁ
    wੵΈ*TTVFʹ͍ͯͨ͠
    w ࣮ճઢͰ࢖͍ͬͯΔݶΓ͸ͦ͜·Ͱ஗͘ײ͍ͯ͡ͳ͔ͬͨ
    w શମ੾Γସ͑લʹ͸ͪΌΜͱ΍Ζ͏ͱࢥ͍ͬͯͨ

    View Slide

  40. ϘτϧωοΫ
    w+BWB4DSJQUϑΝΠϧͷμ΢ϯϩʔυ࣌ؒ
    w ϑΝΠϧͰ.Φʔόʔ
    w XFCQBDLͰ͢΂͕ͯόϯυϧ͞ΕͨϑΝΠϧʹͳΔͨΊ
    w 443͍ͯ͠Δͷʹ+4ϑΝΠϧ͕ϘτϧωοΫʁ
    w 3FBDUIZESBUF׬ྃ·Ͱ͸%0.$POUFOU-PBEFEʹͳΒͳ͍
    ʢৄࡉ͸ޙड़ʣ

    View Slide

  41. $PEF4QMJUUJOH
    w7FOEPS$PEF4QMJUUJOH
    w%ZOBNJD*NQPSUT
    ·ͣ͸͔ͬͪ͜Β

    View Slide

  42. 7FOEPS$PEF4QMJUUJOH
    wࢦఆͨ͠ϞδϡʔϧΛผϑΝΠϧʹ੾Γग़͢
    w XFCQBDLDPOpHʹϧʔϧϕʔεͰઃఆ͢Δ
    wQSPTDPOT
    w ࣮૷Λมߋ͢Δඞཁ͕ͳ͍
    w ϧʔϧ͕૿͑ΔͱΧΦε
    w ෼ׂͰ͖Δཻ౓͕ߥ͍

    View Slide

  43. $PNNPOT$IVOL1MVHJO
    w$PEFTQMJUUJOH༻ͷXFCQBDLQMVHJO
    w XFCQBDLWͰ͸4QMJU$IVOLT1MVHJOʹͳ͍ͬͯΔ
    w XFCQBDLFSͰ͸QSFͰXFCQBDLWରԠ
    w ౰࣌XFCQBDLFS͸WʹରԠ͍ͯ͠ͳ͔ͬͨ
    w ෳ਺ͷϧʔϧΛఆٛ͢Δ৔߹෼͔ΓͮΒ͍

    View Slide

  44. $PNNPOT$IVOL1MVHJO
    clientPlugins.push(
    new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: (m) => /node_modules/.test(m.context)
    }),
    );
    clientPlugins.push(
    new webpack.optimize.CommonsChunkPlugin({
    name: 'd3_chunk',
    minChunks: (m) => /node_modules\/(?:d3|redux)/.test(m.context)
    }),
    );
    clientPlugins.push(
    new webpack.optimize.CommonsChunkPlugin({
    name: 'redux_chunk',
    minChunks: (m) => /node_modules\/(?:redux)/.test(m.context)
    }),
    );
    ໊લϕʔεͰϚον
    ޷͖ͳཻ౓Ͱ
    DIVOLʹͰ͖Δɺ͕
    Ϛονͷ฼ूஂ͕
    લճͷϚονର৅ͳͷͰ
    ্ྲྀ͸ԼྲྀΛશؚͯΉ

    View Slide

  45. #VOEMF"OBMZ[FS1MVHJO
    $PNNPOT$IVOL1MVHJO
    ϧʔϧΛௐ੔ͯ͠
    ࠷େͰ΋,ʹऩ·Δ
    Α͏ʹௐ੔

    View Slide

  46. ͪΐͬͱ଎͘ͳͬͨ
    TT

    View Slide

  47. ΠϯσοΫε
    ͞Ε࢝Ίͨʂʂʂ

    View Slide

  48. ͲΜͲΜ෼ׂ

    View Slide

  49. XFCQBDLDPOpHͷ෼཭
    w$43༻ͱ443༻ͰDPOpHΛ෼཭
    w $PEFTQMJUUJOH͕ඞཁͳͷ͸$43͚ͩ
    w 443࣌͸DIVOL͕Θ͔Ε͍ͯΔͱNBOJGFTU͔ΒEJHFTUͷղܾ΋͢Δඞཁ
    ͕͋Δ
    w ͦ΋ͦ΋෼͚ΔϝϦοτ͕͋·Γແ͍
    w 6OJWFSTBM+4͕΍Γ΍͘͢ͳΔ
    w ͨͩBTTFUDPNQJMFͷ࣌ؒ͸৳ͼΔɾɾɾ

    View Slide

  50. 7FOEPS$PEF4QMJUUJOHͷน
    wґଘؔ܎͸อূ͞Εͳ͍
    w BTZODMPBEͰ͖ͳ͍
    w ແཧ΍Γ෼ׂ͢ΔͱյΕΔ
    wDIVOL͸ϥΠϒϥϦͷαΠζΑΓখ͘͞Ͱ͖ͳ͍
    w ར༻͍ͯ͠ͳ͍ͱ͜Ζ΋όϯυϧ͞Εͯ͠·͏
    wDIVOL෼ׂ͕৬ਓܳԽ

    View Slide

  51. $PEF4QMJUUJOH
    w7FOEPS$PEF4QMJUUJOH
    w%ZOBNJD*NQPSUT ΍ͬͺΓ͔ͬͪ͜

    View Slide

  52. %ZOBNJD*NQPSUT
    import(/* webpackChunkName: "raven" */ 'raven-js').then((Raven) => {
    Raven.config(
    'endpoint',
    {
    environment: this.props.railsEnv,
    release: this.props.revision
    }
    ).install()
    })

    View Slide

  53. %ZOBNJD*NQPSUT
    wQSPTDPOT
    w ґଘੑΛߟྀͰ͖Δ
    w ͖Ίࡉ΍͔ʹ෼ׂ͠΍͍͢
    w EZOBNJDJNQPSUTΛߟྀ࣮ͨ͠૷͕ඞཁ
    w ର৅ΛҰՕॴͰ΋௨ৗJNQPSU͢Δͱ෼ׂ͞Εͳ͍
    w ίʔϧόοΫ੍ޚͭͭ͠ίϯϙʔωϯτߏங͢ΔͭΒΈ

    View Slide

  54. SFBDUMPBEBCMF
    w3FBDUίϯϙʔωϯτΛEZOBNJDJNQPSUT
    ʹͯ͘͠ΕΔ)0$ϥΠϒϥϦ
    import Loadable from 'react-loadable'
    const Lightbox = Loadable({
    loader: () => import(/* webpackChunkName: "react-images" */ 'react-images'),
    loading() {
    return Loading...
    }
    })
    ˞MPBEBCMFDPNQPOFOUTͱ͔΋Αͦ͞͏

    View Slide

  55. SFBDUMPBEBCMF
    wQSPTDPOT
    w ίϯϙʔωϯτΛ؆୯ʹEZOBNJDJNQPSUTʹͰ͖Δ
    w ͢Ͱʹ)0$ʹͳ͍ͬͯΔ΋ͷͱ૬ੑ͕ѱ͔ͬͨΓ͢Δ
    w 6*ܥʹద༻͢Δͱ͔ͤͬ͘443Ͱදࣔ͞ΕͯΔͷʹޙ͔
    ΒҰॠ-PBEJOHʹͳͬͨΓ
    w ґଘؔ܎͕ෳࡶͳίϯϙʔωϯτͰΧΦεʹͳΓ΍͍͢

    View Slide

  56. ݁ہ๚ΕΔίʔϧόοΫ஍ࠈ
    wBXBJUͰ੍ޚ͍ͨ͠
    w ݸผʹBTZODGVODUJPOʹͰ͖Δͱ͜Ζ͸ྑ͍
    w IZQFSOPWB͔ΒHFU$PNQPOFOU͞ΕͯCVJME͍ͯ͠
    ͘Ͳ͔͜ͰٵऩͰ͖ͨΒɾɾɾΜʁ

    View Slide

  57. IZQFSOPWBͳΒ͍͚Δ
    wճSFOEFSͷͨΊʹͦ΋ͦ΋1SPNJTFͰ
    ฦ͍ͯͨ͠
    w CVJME$PNQPOFOUࣗମΛBTZODʹͨ͠
    w ඞཁͳՕॴͰͷBXBJU͕΍Γ΍͘͢ͳͬͨ
    w ػೳ͝ͱʹάϧʔϐϯάͯ͠DIVOL෼ׂ

    View Slide

  58. TDSJQUͷBTZODϩʔυ
    wTDSJQUࣗମΛBTZODʹͯ͠μ΢ϯϩʔυɾ
    ࣮ߦΛඇಉظʹ͢Δ
    w $PNNPOT$IVOL1MVHJOͰ͸ґଘੑΛ୲อͰ͖ͳ
    ͘ͳΔͷͰ࢖͑ͳ͔ͬͨ
    w )5.-ύʔεΛ଴ͨͣʹμ΢ϯϩʔυͰ͖Δ

    View Slide

  59. ·͊·͊଎͘ͳͬͨ
    TT
    ʢΑ͏΍͘ʮී௨ʯ൑ఆʣ

    View Slide

  60. ͲͷλΠϛϯάͰϩʔυ׬ྃͱͳΔ͔ʁ
    w͜Ε·Ͱ%0.$POUFOU-PBEFE΍MPBEΠϕ
    ϯτΛ໨҆ʹ͍ͯͨ͠
    wMPBEΠϕϯτͷλΠϛϯάΑΓUFTUNZTJUF
    ͷධՁ͕஗͍ʁ
    w MPBEΠϕϯτ͕Φ΢νΑΓ஗͍αΠτͰ΋UFTUNZTJUF
    ͷධՁ͕଎͍ͱ͜Ζ΋͋ͬͨ

    View Slide

  61. ը૾ͷ஗ԆϩʔυΛʢҰ෦ʣ΍ΊΔ
    wը૾͸+4Ͱ஗ԆಡΈࠐΈ͍ͯͨ͠
    w 443ͷஈ֊Ͱ͸μϛʔͷը૾͔͠ೖΒͳ͍
    w 3FBDUIZESBUF͕׬ྃ͢Δ·ͰϑΝʔετϏϡʔͷը૾μ
    ΢ϯϩʔυ͕։࢝͞Εͳ͍
    wϑΝʔετϏϡʔ͚ͩ஗ԆϩʔυΛ΍Ίͨ
    w 443͍ͯ͠ΔͷͰ+4ͷධՁΛ଴ͨͣʹը૾͕දࣔ͞ΕΔ

    View Slide

  62. ΘΓͱ଎͘ͳͬͨ
    TT
    ʢʮྑ޷ʯ൑ఆʣ

    View Slide

  63. ଎͘ͳΔʹͭΕͯ
    ΠϯσοΫε΋૿͑ͨ

    View Slide

  64. ݁࿦
    w41"Ͱ͋ͬͯ΋4&0ͷجຊ͸ಉ͡
    w ద੾ͳαΠτߏ଄΍λάઃఆΛߦ͏
    w දࣔ଎౓Λ্͛Δ
    w ஗͍ͱΠϯσοΫε͞Εͳ͍Մೳੑ͕͋Δ
    w ଎͚Ε͹443͸ඞਢͰ͸ͳ͍͔΋

    View Slide

  65. 41"ͷύϑΥʔϚϯε͸ཁ஫ҙ
    wωοΫʹͳΓ΍͍͢ϙΠϯτ
    w +4ͷϑΝΠϧαΠζ
    w γʔέϯγϟϧͳ"1*ίʔϧ
    w$43Ͱಈ͔͍ͯ͠Δͱؾ͖ͮͮΒ͍৔߹͕͋Δ
    w ࠩ෼͚ͩมߋ͞Ε͍ͯͨ͘Ί
    w443͸଎౓ͷͨΊͷҰͭͷखஈ

    View Slide

  66. 443ͷϝϯςφϯε͸݁ߏେม
    w։ൃத͸$43Ͱಈ͔͍ͯ͠Δ
    w ͏͔ͬΓXJOEPX৮ͬͨΓͯ͠΋ؾ͔ͮͳ͍
    w443 /PEFKTଆ
    ͰΤϥʔ͕ग़ͨΒࣗಈͰ$43ʹͳΔΑ͏ʹͯ͠
    ͍Δ
    w ϢʔβʔӨڹແ͍෼ؾ͖ͮͮΒ͍
    wTUBHJOHσϓϩΠ͞ΕͨΒ&&ςετͰ443͞Ε͍ͯΔ͔νΣοΫ
    w ͳΔ΂͘ૣΊʹݕ஌

    View Slide

  67. 8FSFIJSJOH
    ࣍ͷΦ΢νʔϊΛ͍ͬ͠ΐʹ࡞Γ·ͤΜ͔
    SFDSVJU!PVDDJOPKQ
    IUUQTDPSQPSBUFPVDDJOPKQSFDSVJU
    ։ൃऀϒϩάIUUQTEFWFMPQFSTPVDDJOPDPN

    View Slide