ジャムスタックチョットデキル 2023/09/09
7 principles for rich web appsAnd how next.js achievesthese principles@ δϟϜελοΫνϣοτσΩϧΧϯϑΝϨϯε in தyosuke_furukawa / 2023-09-09
View Slide
X(Twitter): @yosuke_furukawaGithub: yosuke-furukawa
JSConf.jp ticket pageis open.ࠓΦϑϥΠϯͱΦϯϥΠϯͷϋΠϒϦου։࠵༧ఆΦϯϥΠϯ10݄ࠒʹެ։͠·͢ɻઌߦͯ͠དྷͯ͘ΕΔํࠓͷϖʔδ͔Βདྷ͍ͯͩ͘͞ʂ
Looong Longtime ago...From 2014 ※
ϦονͳΞϓϦέʔγϣϯͷͨΊͷ7ͭͷݪଇͱ͍͏λΠτϧͰ༁هࣄΛग़ͨ͠• Vercel ݱ CEO ͷ Guillermo Rauch ࢯ͕ॻ͍ͨهࣄΛ༁ͨ͠ͷ• ࠓͰࢀߟʹͳΔΑ͏ͳݪଇ
ϦονͳΞϓϦέʔγϣϯͷͨΊͷ7ͭͷݪଇ1.αʔόʔ͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍ɻ2.ϢʔβʔೖྗʹਝʹରԠ͠Α͏3.σʔλͷมߋʹԠ͠Α͏4.σʔλมߋΛαʔόʔͱͱʹίϯτϩʔϧ͠Α͏5.historyΛյ͖͢͡Όͳ͍ɺhistoryΛ֦ு͠Α͏6.ίʔυͷߋ৽ΛPush͠Α͏7.ৼΔ͍Λ༧ଌ͠Α͏
ϦονͳΞϓϦέʔγϣϯͷͨΊͷ7ͭͷݪଇ1.αʔόʔ͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍ɻ2.ϢʔβʔೖྗʹਝʹରԠ͠Α͏3.σʔλͷมߋʹԠ͠Α͏4.σʔλมߋΛαʔόʔͱͱʹίϯτϩʔϧ͠Α͏5.historyΛյ͖͢͡Όͳ͍ɺhistoryΛ֦ு͠Α͏6.ίʔυͷߋ৽ΛPush͠Α͏7.ৼΔ͍Λ༧ଌ͠Α͏ /FYUKT͜ΕΒͷݪଇΛ͍͔ͭ͘ୡ͍ͯ͠Δ
αʔόʔ͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍
αʔό͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍ɻ•ҙͰͳ͍ => ඞਢͰ͋Δ•ͭ·ΓϦονͳΣϒΞϓϦέʔγϣϯʹࣄલͷϨϯμϦϯά͕ඞਢͰ͋Δͱݴ͍ͬͯΔɻ•ͳ͔ͥʁ
αʔό͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍ɻ•ϥϯυτϦοϓʹ͔͔Δ࣌ؒΛͬͯ·͔͢ʁ•࣌ؒɿ•Stanford͔ΒBoston·Ͱޫͷ͞Ͱ43.2ms•ޫϑΝΠόʔͰߦ͘ͱ85ms͔͔Δ
αʔό͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍ɻ•ϥϯυτϦοϓճΛͬͯ·͔͢ʁ•CSRͱSSRͰൺֱ͢Δ4FSWFSCSRۭͷHTMLऔಘJavaScriptऔಘAPI fetch4FSWFSSSRHTMLऔಘJavaScriptऔಘ
αʔό͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍ɻ•ϥϯυτϦοϓճΛͬͯ·͔͢ʁ•CSRͱSSRͰൺֱ͢Δ4FSWFSCSRۭͷHTMLऔಘJavaScriptऔಘAPI fetch4FSWFSSSRHTMLऔಘ$43ճ443ճ˞ݫີͳͰͳ͍ɻ5$1ͷϥϯυτϦοϓͱ͍͏ҙຯͰճҟͳΔɻ
αʔό͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍ɻ•ݴ͍͍ͨͷ:•ࣄલʹϨϯμϦϯά͠ͳ͍ͱϢʔβʔͷͪ࣌ؒ͘ͳͬͯ͠·͏ͱ͍͏͜ͱ•SEOͱ͔ͦ͏͍͏͜ͱͱແؔʹ࠷దͳUXΛఏڙ͢ΔͳΒࣄલʹϨϯμϦϯάΛ͢Δํ͕ྑ͍
αʔό͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍ɻ•Next.js͢Ͱʹ͜ͷ෦ΛϨϯμϦϯάʹؔ͢ΔιϦϡʔγϣϯΛ͍͔ͭ͘ఏڙ͢Δ͜ͱͰୡ͍ͯ͠Δɻ•SSR•SSG•ISR•͞Βʹ Vercel ͱΈ߹ΘͤΔͱCDNͰͷΩϟογϡ༗ޮʹɻ
ϢʔβͷೖྗʹਝʹԠ͠Α͏
ϢʔβʔͷೖྗʹਝʹԠ͠Α͏•͖ͬ͞ͷϨϯμϦϯά͕දࣔ͢Δ·Ͱͷ࣌ؒʹରͯ͠ͷͩͱ͢Δͱͬͪ͜ૢ࡞ʹରͯ͠ͷ•ૢ࡞ͨ͜͠ͱʹରͯ͠ͷϑΟʔυόοΫΛૣ͘Ͱ͖ΔΑ͏ʹ͢Δ
ϢʔβʔͷೖྗʹਝʹԠ͠Α͏•࣮ͷճΓʹ͋ΔΞϓϦέʔγϣϯΠϯλϥΫγϣϯʹରͯ͠ߴʹԠ͢ΔΑ͏ʹग़དྷ͍ͯΔɻGoogleͷྫ
ϢʔβʔͷೖྗʹਝʹԠ͠Α͏•્ͯ͠͠·͏ཁҼʹͳΔͷͳʹ͔•ڊେͳ JavaScript ϑΝΠϧ͕͋Δͱ࣮ૢ࡞Ͱ͖ΔΑ͏ʹͳΔ·Ͱͷ͕࣌ؒඇৗʹ͘ͳΔ•ͦͦ͜͏͍͏շదͳϑΟʔυόοΫΛͨΒ͢ͷJSͷ࡞༻ʹΑΔӨڹ͕େ͖͍•ͳͷʹͰ͔͍JS͕͋ΔͱίϯύΠϧ=>࣮ߦͰͨ͘͞Εͯ͠·͏
ϢʔβʔͷೖྗʹਝʹԠ͠Α͏•Next.jsίʔυεϓϦοτϖʔδ୯ҐͰεϓϦοτͯ͘͠Ε͍ͯΔ•/settings ʹඞཁͳίʔυͱ /login ʹඞཁͳίʔυΛ͚ͯඞཁͳϖʔδʹ͚ͩඞཁͳίʔυؚ͕·ΕΔΑ͏ʹ͍ͯ͠Δ•͔݁͠ߏ಄͕ྑ͍ɻ React / react-dom ͳͲͷසग़͢ΔϥΠϒϥϦͷͷͲ͜Ͱར༻͞ΕΔͷͰͦΕڞ௨Խ͞Εͨίʔυͱͯ͠࠶ར༻͞Εׂ͘͢͞Ε͍ͯΔɻ
σʔλͷมߋʹԠ͠Α͏
σʔλͷมߋʹԠ͠Α͏•ϢʔβϏϦςΟͷ•σʔλ͕มߋ͞Ε͍ͯΔͷʹΘ͟Θ͟ϦϩʔυϘλϯΛԡ͞ͳ͍ͱ͍͚ͳ͍Α͏ͳ࡞Γʹ͢Δ͖Ͱͳ͍•Guillermo Rauchࢯͷ͏Ұͭͷஶ໊ϥΠϒϥϦ socket.io ͳͲΛར༻ͯ͠σʔλͷมߋʹଈ࠲ʹԠ͢Δ͖ͱ͍͏•͋Μ·ΓNext.jsؔͳ͍
σʔλͷมߋʹԠ͠Α͏•ݴ͏қ͘ߦ͏͠•Firestoreͱ͔socket.ioͱ͔Λۦ͢Δײ͡ʹͳΔ
σʔλมߋΛαʔόʔͱͱʹίϯτϩʔϧ͠Α͏
σʔλมߋΛαʔόʔͱͱʹίϯτϩʔϧ͠Α͏•͜ΕϢʔβϏϦςΟͷ•Service Worker ܥ•σʔλૹ৴ͱ͔ʹࣦഊͯ͠ϦτϥΠ͢ΔΈΛ࡞ͬͨΓ•όοΫάϥϯυͰσʔλಉظͯ͠ΩϟογϡΛΦϑϥΠϯͰ͑ΔΑ͏ʹ͠Α͏ͱ͍͏•Next.js͋Μ·Γؔͳ͍
σʔλมߋΛαʔόʔͱͱʹίϯτϩʔϧ͠Α͏•͜͏͍͏ʮ͓ͯͳ͠ʯ͕͋Δͱྑ͍ͱ͍͏খ͍͚͞Ͳେࣄͳ•ྫ͑ɿ•Ϣʔβʔ͕ΦϑϥΠϯʹͳ͔ͬͨͲ͏͔ navigator.onLine ͰଌΕΔɻΦϑϥΠϯʹͳͬͯΔͳΒͦΕΛදࣔͯ͋͛͠Δ͖•APIͷλΠϜΞτͱ͔ϒϥβͤʹͯ͠ͳ͍͔ʁࣗͨͪͰࣄલʹλΠϜΞτΩϟϯηϧΛ࣮ͨ͠΄͏͕ྑ͍•403ΤϥʔʢೝূΤϥʔʣ͕ൃੜ͍ͯ͠ΔͳΒϩάΠϯը໘Λݟͤͯ͋͛Δ͖•͏͔ͬΓϒϥβดͨ࣌͡ʹ beforeUnload ͰҰճ֬ೝ͢Δͱ͔
ɾσʔλͷมߋʹԠ͠Α͏ɾσʔλมߋΛαʔόʔͱͱʹίϯτϩʔϧ͠Α͏͜ͷ̎ͭͷݪଇNext.jsؔͳ͍͚ͩͲɺ࣮ͷ্ͰؾΛ͚ͭΔ͖ϙΠϯτ
historyΛյ͖͢͡Όͳ͍ɺhistoryΛ֦ு͠Α͏
historyΛյ͖͢͡Όͳ͍ɺhistoryΛ֦ு͠Α͏•Single Page Application ͋Δ͋Δɺhistory͕յΕͨUI•FYBNQMFDPNFYBNQMFDPNFYBNQMFDPNεΫϩʔϧ͢ΔϦϯΫΛΫϦοΫͯ͠ભҠ͢Δ ભҠޙʹΔεΫϩʔϧҐஔ͕࠷ॳʹΔ
historyΛյ͖͢͡Όͳ͍ɺhistoryΛ֦ு͠Α͏•ଞʹ:•͍͍Ͷ͚ͨ͠Ͳɺ͍͍Ͷ͕ͬͨΒফ͑ͯͨʢ࣮ࡍʹՃ͞ΕͯΔʣ•ΞίʔσΟΦϯ։͍ͨޙʹϖʔδભҠͯͬͨ͠ΒΞίʔσΟΦϯดͯͯ͡ભҠ͕յΕΔͱ͔
historyΛյ͖͢͡Όͳ͍ɺhistoryΛ֦ு͠Α͏•͜ΕԿͰى͖Δ͔ͱݴ͏ͱɿෳ߹తͳཁҼ•ϖʔδભҠલʹ͍ΔҐஔΛϒϥβຊͳΒ֮͑ͯΔ•͚ͩͲͬͨ࣌ʹ history api ΛͬͯͯΔͷ͕ૣͯ͘ίϯςϯπ͕·ͩͳ͍ͱ͔ͦ͏͍͏࣌ʹى͖Δ•BFCacheͱ͔Navigation API ͱ͔ Scroll Resolution API ͱ͔ͦ͏͍͏ϒϥβ͔Βͷαϙʔτ૿͑ͯΔ͕·ͩ͑ͳ͍ڥଟ͍ͷͰɺ࣮ࡍʹ݁ߏͪ͜ΒଆͰͳΜͱ͔͠ͳ͍ͱ͍͚ͳ͍
historyΛյ͖͢͡Όͳ͍ɺhistoryΛ֦ு͠Α͏•Next.jsͩͱεΫϩʔϧҐஔΛ͢ͱ͍͏ҙຯͰɾɾɾ•Page router εΫϩʔϧҐஔΛ֮͑ͯ͘Εͯͯɺϖʔδ͕ϨϯμϦϯά͞Ε͔ͯΒεΫϩʔϧͯ͘͠ΕΔ•Experimental Ͱ Scroll Restoration ϓϩύςΟͰͷ੍ޚͯ͘͠ΕΔɻ•App routerݱ࣌ͩͱͦͷ͋ͨΓͷ͜ͱΩϟογϡʹͤͯΔʢϒϥβʹͤͯΔײ͡ʁʣ•εΫϩʔϧճؼҎ֎ͷ͜ͱԿͯ͠ͳ͍ͷͰɺ࣮ʹؾΛ͚ͭͳ͍ͱ͍͚ͳ͍•ࣾͰOSSͰϥΠϒϥϦ࡞ͬͯղܾ͢Δਓͨͪνϥϗϥ•https://github.com/recruit-tech/location-state
ίʔυͷมߋΛPush͠Α͏
ίʔυͷมߋΛPush͠Α͏•σʔλ͕ߋ৽͞Εͨʹ͔͔ΘΒͣɺίʔυ͕ͦͷ··ͩͱͲ͏ͳΔ͔•ϨϯμϦϯά͕͏·͘ߦ͔ͳ͍Մೳੑ͕͋Δ•ը໘͕ΤϥʔʹͳΔ•த్ͳঢ়ଶͰϨϯμϦϯά͞ΕΔՄೳੑ͕͋Δ•͜͏͍͏ͷΛආ͚ͳ͚Ε͍͚ͳ͍ɻ
ίʔυͷมߋΛPush͠Α͏•։ൃڥͩͱ Hot code reload ͳͲͷํ๏ͰίʔυͷมߋΛpushͯ͘͠ΔͷͰΘ͔Γ͍͢•ຊ൪ڥͩͱ࣮ͳ͔ͳ͔͍͠•Τϥʔʹͳͬͨ࣌ʹҰճ৽͘͠ϖʔδΛϋʔυϦϩʔυͤ͞Δͱ͔ͷख͋ΔͬͪΌ͋Δɻ•ਅ໘ʹΖ͏ͱ͢ΔͱversionͷࠩҟΛݕͯ͠Ͳ͏͢Δ͔Λ࣮͠ͳ͍ͱ͍͚ͳ͍ɻΤϥʔ͕ى͖͚ͨͩͰஅ͢ΔͱϦϩʔυ͕ଟ͘ͳΔ
ίʔυͷมߋΛPush͠Α͏•Next.js ͩͱ getServerSideProps ͳͲͷΈࠐΈͷσʔλ fetch ؔΛͬͯΔ߹ʹෆ߹͕͋ΔͱversionࠩҟΛݕͯ͠Ϧϩʔυͯ͘͠ΕΔ•ͨͩ͜ͷػೳͪΖΜ PageRouter ͷΈ•App Router ͩͱ Vercel ͕ఏڙͯ͘͠Ε͍ͯΔ Skew Protection Λ͏ඞཁ͕͋Δʢࣗ͘͠લͰ࣮ʣ•
ৼΔ͍Λ༧ଌ͠Α͏
ৼΔ͍Λ༧ଌ͠Α͏•Ϣʔβʔ͕࣍ʹԿΛ͢Δͷ͔Λ༧ଌͯ͠proactiveʹৼΔ͏•ྫ͑ Google Map ɺͱਐΉͱ࣍ʹͷํʹϦΫΤετ͕དྷΔͱ༧ଌͯ͠ઌճΓͯ͠σʔλΛऔಘ͢Δ•Google ݕࡧ༧ଌݕࡧͷ݁ՌΛ prefetch ͯ͠Δ•͜͏͍͏ಈ͖ΛೖΕΔ͖ͩͱ͍͏ɹ
ৼΔ͍Λ༧ଌ͠Α͏•एׯͦ͠͏ʹݟ͑Δ•Γ͗͢ΔͱσʔλΛ༨ʹऔΓͦ͏͕ͩɺαʔόͷϦιʔεΛػతʹ͍ͭͭɺϢʔβͷ͓ͯͳ͠Λߦ͏ͱ͍͏όϥϯεΛͲ͏औΔ͔•ͪͳΈʹ Next.js େϢʔβʔͷ͓ͯͳ͠ʹدͬͯΔɻ
ৼΔ͍Λ༧ଌ͠Α͏•Next.js next/link Λ͏ͱϢʔβͷviewportʹೖͬͨॠؒʹprefetch͠ʹ͍͘•େػత͕ͩɺͦΕ͕ Next.js ΛͬͨαʔϏε͕ߴʹݟ͑Δཧ༝ͩͬͨΓ͢Δ•ҰํͰؾΛ͚ͭͳ͍ͱϢʔβͷΪΨୣͬͯͦ͏ͩ͠ɺαʔόʹෛՙ͔͔ͬͯΔɺαʔόͷϦΫΤετ͕૿͑ͯͯίετ͕͔͔ͬͯͳ͍͔֬ೝ͓ͯ͘͠ͱྑ͍
·ͱΊ
·ͱΊ1.αʔόʔ͕ϖʔδΛϨϯμϦϯά͢ΔͷҙͰͳ͍ɻ => SSR/SSG/ISRͳͲιϦϡʔγϣϯଟ2.ϢʔβʔೖྗʹਝʹରԠ͠Α͏ => ίʔυεϓϦοτ͕σϑΥϧτͰ࣮͞ΕͯΔ3.σʔλͷมߋʹԠ͠Α͏ => Next.js ͰͷରԠͳ͍͕ɺsocket.ioͳͲͱΈ߹Θ࣮ͤͯͰ͖Δ
·ͱΊ4.σʔλมߋΛαʔόʔͱͱʹίϯτϩʔϧ͠Α͏ => Next.js ͰͷରԠͳ͍͕ɺService WorkerͳͲͱΈ߹ΘͤΑ͏5.historyΛյ͖͢͡Όͳ͍ɺhistoryΛ֦ு͠Α͏ => Page Router ͷ߹ɺ࠷ݶͷεΫϩʔϧҐஔΛ֮͑ͯΔɺ App Router Ωϟογϡ͕ڧ͍ͷͰͦΕʹͤͯΔ6.ίʔυͷߋ৽ΛPush͠Α͏ => Next.js page router ͰΈࠐΈͷσʔλϑΣονؔΛ͑ݕͯ͘͠ΕΔ͕ɺ approuter ͷ߹ vercel ʹཔΔܗʹͳΔ͔ʢSkew Protectionʣ7.ৼΔ͍Λ༧ଌ͠Α͏ => next/link ͰػతʹσʔλΛprefetchͯ͘͠ΕΔ
·ͱΊ• ϦονͳΞϓϦέʔγϣϯͷͨΊͷ7ͭͷݪଇΛվΊͯհͭͭ͠ɺNext.jsͷػೳΛհͨ͠ɻ• Next.js ͳΜͰ͏Μ͚ͩͬʁͬͯͳͬͨ࣌ʹʮϦονͳΞϓϦέʔγϣϯͷͨΊͷݪଇʯʹै͏ͨΊͩͱ͍͏ҙݟͰઆಘࡐྉʹ͑Δͱࢥ͏ɻ• ͪΖΜࣗͨͪͰ͜ΕΒΛ࣮Ͱ͖Δͱࢥ͏ͷͰઈରͷඞཁ݅ʹͳΓಘͳ͍ɻ• ͜Ε͔Β৭ʑΞοϓσʔτ͞Ε͍ͯ͘ͱࢥ͏ͷͰࢹ͍ͯ͘͠༧ఆ