Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Vue.js 2.0 Server Side Rendering
Search
kazupon
November 13, 2016
Programming
12k
46
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Vue.js 2.0 Server Side Rendering
Nodefest 2016 Nov 13
kazupon
November 13, 2016
More Decks by kazupon
See All by kazupon
プラグインで拡張される Context をtype-safe にする難しさと設計判断
kazupon
2
1k
Oxlint JS plugins
kazupon
1
1.4k
gunshi
kazupon
1
240
Nitro v3
kazupon
2
560
わたしのOSS活動
kazupon
3
630
Vapor Revolution
kazupon
3
4.2k
Vue.js最新動向
kazupon
3
1.6k
Vue 3.4
kazupon
13
4.9k
Vue & Vite Rustify
kazupon
4
2.5k
Other Decks in Programming
See All in Programming
ローカルLLMでどこまでコードが書けるか -縮小版 / How much code can be written on a local LLM Shortened
kishida
2
190
【やさしく解説 設計編 #1】「ドメイン駆動」と「実装駆動」ってなに? 〜設計の考え方を、たとえ話で学ぼう〜
panda728
PRO
1
120
型も通る、synthも通る、それでも危ない 〜AIのCDKの権限とコストを機械で検証する〜 / It Passes Type Checks, It Passes Synth Checks, but It’s Still Risky — Automatically Verifying Permissions and Costs in AI’s CDK —
seike460
PRO
1
360
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
500
act2-costs.pdf
sumedhbala
0
110
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
12
4.8k
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
130
AIエージェントで 変わるAndroid開発環境
takahirom
2
680
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
850
OSINT for SRE: 学術論文とポストモーテムから探る システム障害の共通パターン / SRE NEXT 2026
tomoyk
1
3.9k
yield再入門 #phpcon
o0h
PRO
0
500
コーディングルールの鮮度を保ちたい for SRE NEXT 2026 / keep-fresh-go-internal-conventions-sre-next-2026
handlename
0
140
Featured
See All Featured
Testing 201, or: Great Expectations
jmmastey
46
8.2k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
370
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.4k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
420
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
240
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
The World Runs on Bad Software
bkeepers
PRO
72
12k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
260
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.5k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.4k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
350
Transcript
Vue.js 2.0 αʔόαΠυϨϯμϦϯά ౦ژNodeֶԂࡇ2016 2016-11-13 @kazupon
ࣗݾհ
Vue.js Core Team member https://github.com/kazupon
vuejs-jp ༗ࢤͰٕज़ධ༷ࣾʹͯ Web هࣄ࿈ࡌத http://gihyo.jp/dev/serial/01/vuejs
We are building Open Innovation Platform!! https://cuusoo.com
Vue.js ͱʁ
࡞ऀ Evan You ※ ࡞ऀͷΠϯλϏϡʔهࣄ: Between the Wires | Evan
You https://betweenthewires.org/between-the-wires-evan-you-cb56660bc8a4#.6b0vzpgzw ※ ࡞ऀͷαΠτ http://evanyou.me
ϓϩάϨογϒϑϨʔϜϫʔΫ
※ CC BY Evan You https://docs.google.com/presentation/d/1WnYsxRMiNEArT3xz7xXHdKeH1C-jT92VxmptghJb5Es/edit#slide=id.p ※ ԼهURL࿈ࡌهࣄ http://gihyo.jp/dev/serial/01/vuejs/0001?page=1 vue-router
vuex vue-loader vueify vue-cli কདྷ ఏڙ༧ఆ vue.js ຊମ
• 201610݄1(ຊ࣌ؒ) ίʔυωʔϜ: Ghost in the Shell ϦϦʔεʂ ࠷৽όʔδϣϯ 2.0
2017 2014 2015 2016 spawn 0.6 0.8 0.9 0.10 0.11 0.12 1.0 2.0 2013 ※ Πϥετஶ࡞ऀ: @hashedrock ࢯ https://twitter.com/hashedrock/status/782069763358924800 Animatrix Blade Runner Cowboy Bebop Dragon Ball Evangelion ghost in the shell
2.0 Ͱͷେ͖͍มߋ
ϨϯμϦϯάγεςϜ͕৽ʂ • Virtual DOM ΞʔΩςΫνϟΛ࠾༻ • Evan You ࢯʹΑΔϑϧεΫϥον࣮ •
2िؒͨΒͣͰpre-alphaόʔδϣϯΛ࣮
Virtual DOM ʹΑΔޮೳ
ϨϯμϦϯάͷߴԽ ※ The Vue Point: Vue 2.0 is Here (Vue.js
ެࣜϒϩά) https://medium.com/the-vue-point/vue-2-0-is-here-ef1f26acf4b8#.v9xl2x8f7
αʔόαΠυϨϯμϦϯά Request Response Client Server render
ॊೈͳϨϯμϦϯάखஈ • એݴతϨϯμϦϯά • ςϯϓϨʔτ • ໋ྩతϨϯμϦϯά • ԾϊʔυΛར༻ͨ͠ JavaScript
࣮ • hyperscript / JSX
ΫϩεϓϥοτϑΥʔϜରԠ • ϚϧνϞόΠϧσόΠε͚ͷϑϨʔϜϫʔΫ ※ weex https://alibaba.github.io/weex/
ΞδΣϯμ
ࠓ͢͜ͱ • ϨϯμϦϯάγεςϜ • αʔόαΠυϨϯμϦϯά
ϨϯμϦϯάγεςϜ
Vue ͷΑ͋͘ΔҰൠతͳίʔυ <html> <head> ... <script src="./vue.js"></script> </head> <body> ...
<div id="app"></div> <script> new Vue({ template: `<div> <p>message: {{msg}}</p> </div>`, data: { msg: 'hi evan!!' } }).$mount('#app') </script> ... </body> </html>
෦Ͱ৭ʑͱΰχϣΰχϣ͢Δ ॳظϨϯμϦϯάྃ ࠶ϨϯμϦϯά
ҰൠతͳϨϯμϦϯάͷྲྀΕͰ ෦ʹ͍ͭͯݟ͍͖ͯ·͠ΐ͏ʂ
ҰൠతͳϨϯμϦϯάͷྲྀΕ optimize diff + patch compile generate rendering track dependencies
AST Optimized AST Virtual DOM
ίϯύΠϧ
ςϯϓϨʔτΛ AST ʹม • ςϯϓϨʔτύʔαΛར༻ͯ͠AST (Abstract Syntax Tree) ʹม <div
id="app"> <h1>title</h1> <p class="msg"> msg: {{msg}} </p> </div> Template { tag: 'div', type: 1, children: [{ tag: 'h1', type: 1, children: [{ type: 3, text: 'title', … }], … }, { tag: 'p', type: 1, children: [{ type: 2, text: 'msg: {{msg}}', … }], … }] } compile AST
ςϯϓϨʔτύʔα • ςϯϓϨʔτύʔαɺJohn Resig ࢯͷͷΛ fork ͠ ͯ Vue ͚ʹಠࣗʹΧελϚΠζ
※ ςϯϓϨʔτύʔα https://github.com/vuejs/vue/blob/dev/src/compiler/parser/html-parser.js ES2015 Ͱ re-write & ࠷దԽ
࠷దԽ
Ұൠతͳ Virtual DOM New Virtual Node Tree Old Virtual Node
Tree diff + patch ԾϊʔυπϦʔͷ diff ࢉग़ίετ͕͔͔Δ
• Virtual DOM ʹΑΔϨϯμϦϯάͷύϑΥʔϚϯεΛΑ ͘͢ΔͨΊʹɺVue Ͱ AST ʹରͯ͠ҎԼͷ̎ஈ֊ͷॲ ཧΛͯ͠࠷దԽ͢Δ
1. ੩తͳϊʔυͷݕग़ 2. ੩తͳϊʔυπϦʔͷݕग़ AST ʹର͢Δ࠷దԽॲཧ
1. ੩తͳϊʔυͷݕग़ • ҎԼͷΑ͏ͳςϯϓϨʔτͷέʔεͷ߹ <div id="app"> <div class="header">title: {{title}}</div> <ul
style="list-style-type: none" class="menu"> <li style="margin: 0" :class="{ even: isEven(index), odd: isOdd(index) }" v-for="(item, index) in items"> menu{{index}}: {{item}} </li> </ul> <div class="content"> <p>This is content</p> </div> </div> divɺpɺͦͯ͠ text ཁૉΛ ੩తͳϊʔυͱͯ͠ݕग़
• ݕग़͞ΕͨϊʔυɺAST ʹ static = true ͱͯ͠ϚʔΩ ϯά͞ΕΔ 1. ੩తͳϊʔυͷݕग़
{ tag: 'div', children: [{ tag: 'div', … }, { tag: 'ul', children: […], … }, { tag: 'div', static: true, children: [{ tag: 'p', static: true, children: [{ static: true, text: 'This is content', … }] }], … }] }
2. ੩తͳϊʔυπϦʔͷݕग़ • ੩తͳϊʔυΛࢠͱͯ࣋ͭ͠ϧʔτ(root)ͱͳΔཁૉ͕ ͋Δ͔Ͳ͏͔νΣοΫ͢Δ <div id="app"> <div class="header">title: {{title}}</div>
<ul style="list-style-type: none" class="menu"> <li style="margin: 0" :class="{ even: isEven(index), odd: isOdd(index) }" v-for="(item, index) in items"> menu{{index}}: {{item}} </li> </ul> <div class="content"> <p>This is content</p> </div> </div> ͜ͷέʔεͰɺ͜ͷ div ཁૉ͕ ੩తͳϊʔυπϦʔͱͯ͠ݕग़
2. ੩తͳϊʔυπϦʔͷݕग़ • ݕग़͢ΔͱɺAST ʹ staticRoot = true ͱͯ͠ϚʔΩϯά͢Δ {
tag: 'div', children: [ { tag: 'div', … }, { tag: 'ul', children: […], … }, { tag: 'div', static: true, staticRoot: true, children: [ { tag: 'p', static: true, children: [{ text: 'This is content', static: true, … }], … } ], … } ] }
ϨϯμϦϯάؔͷੜ
AST ͔ΒϨϯμϦϯάؔΛੜ • ҎԼͷؔΛੜ͢Δ • render ؔ • staticRenderFns ؔ܊
(ྻ) • ͜ΕΒؔɺVue Πϯελϯ εͷ $options ʹ֨ೲ͞ΕΔ Optimized AST
render ؔ • ԾϊʔυπϦʔΛฦؔ͢ (e.g. ࠷దԽͰઆ໌ͨ͠ASTΛrenderؔԽͨ͠ͷ) function anonymous () {
with (this) { return _h('div', { attrs: { 'id': 'app' } }, [ _h('div', { staticClass: 'header'}, ['title: 'ɹ+ɹ_s(title)]), _h('ul', { staticClass: 'menu', attrs: { style: 'list-style-type: none' } }, [_l((items), function (item, index) { return _h('li', { ɹɹɹɹɹɹɹɹɹɹkey: item.id, class: { even: isEven(index), odd: isOdd(index) }, attrs: { style:'margin: 0' } }, ['menu' + _s(index) + ': ' + _s(item.name)]) })] ), _m(0) ]) } ࠷దԽʹΑΓݕग़͞Εͨ੩తͳϊʔυπϦʔͷ෦ɺ ෦ϝιουʹϚοϐϯά
staticRenderFns ؔ܊ • ࠷దԽʹΑΓ AST Ͱݕग़ͨ͠੩తͳϊʔυπϦʔɺ ԾϊʔυπϦʔͱͯ͠ฦ͢Α͏ʹੜͨؔ͠Λྻ ʹ֨ೲ͢Δ [ function
anonymous () { with (this) { return _h('div', { staticClass: 'content' }, [ _h('p', ['This is content']) ]) }} ]
ϨϯμϦϯά
ϦΞΫςΟϒγεςϜʹΑΔϨϯμϦϯά diff + patch create call Virtual Node Tree notify
Watcher ʹΑΔσʔλมߋͷࢹ • σʔλґଘؔΛ͠ɺσʔλมߋΛࢹ͢Δ • σʔλมߋݕग़ͷɺ Watcher ෦ͷ _render ϝ
ιουΛݺͼग़ͯ͠࠶ϨϯμϦϯά const Component = { props: ['msg'], data () { return { title: 'hello', items: […] } }, … } msg data/props Watcher title items 0 x … Collect Dependencies
ԾϊʔυπϦʔͷੜ • render ؔͱ staticRenderFns ؔ܊ͰԾϊʔυπϦʔ Λੜ function anonymous ()
{ with (this) { return _h('div', { attrs: { 'id': 'app' } }, [ _h('div', { staticClass: 'header'}, ['title: 'ɹ+ɹ_s(title)]), _h('ul', { staticClass: 'menu', attrs: { style: 'list-style-type: none' } }, [_l((items), function (item, index) { return _h('li', { ɹɹɹɹɹɹɹɹɹɹkey: item.id, class: { even: isEven(index), odd: isOdd(index) }, attrs: { style: 'margin: 0' } }, ['menu” + _s(index) + ": “ + _s(item.name)]) })] ), _m(0) ]) } [ function anonymous () { with (this) { return _h('div', { staticClass: 'content' }, [ _h('p', ['This is content']) ]) }} ] staticRenderFns render
ԾϊʔυπϦʔͷΩϟγϡԽͱϚʔΩϯά • staticRenderFns ͷ࣮ߦ݁ՌͰಘΒΕΔ੩తͳԾϊʔυ πϦʔɺ࠷ॳͷϨϯμϦϯάͷࡍʹ͜ΕΒ݁ՌΛ Vue Πϯελϯε෦ʹΩϟογϡ͢Δ • ͜ͷ࣌ɺԾϊʔυπϦʔʹ isStatic
= true ͰϚʔΩϯ ά͢Δ͜ͱͰɺޙͷ Virtual DOM ͷ diff ʹ͓͍ͯεΩο ϓ͞ΕΔ • ࠶ϨϯμϦϯά࣌ʹɺΩϟογϡԽ͞ΕͨͷΛར༻
Vue ͷ Virtual DOM • Virtual DOM snabbdom Λ
fork ͯ͠ಠࣗʹΧελϚΠ ζͨ͠ͷ https://github.com/snabbdom/snabbdom
Virtual DOM ͷ diff / patch • diff / patch
جຊ snabbdom ϕʔεͰɺVue ͷ࠷దԽॲ ཧʹΑͬͯ diff ΛεΩοϓ͢Δ͜ͱͰɺDOM Λੜ͢Δ ·ͰॲཧίετΛݮ https://github.com/vuejs/vue/blob/dev/src/core/vdom/patch.js
ԾϊʔυπϦʔͷੜ͔Β Virtual DOM ʹΑΔॲཧ·Ͱͷ Πϝʔδ
ॳظͷϨϯμϦϯά return caching & marking create diff + patch skip
diff of the marked tree
ߋ৽࣌ͷ࠶ϨϯμϦϯά return create diff + patch skip diff of the
marked tree
ίϯϙʔωϯτπϦʔ ͷ ϨϯμϦϯά
• ֤ίϯϙʔωϯτຖʹ Watcher Λհͯ͠τοϓμϯత ʹϨϯμϦϯά Ұ൪τοϓ͔Βࢠʹ͔ͬͯϨϯμϦϯά Watcher track dependencies component
Root (new Vue)
• ίϯϙʔωϯτͷ data/props ͕ɺଞʹґଘ͕ͳ͘ɺ ͦͷίϯϙʔωϯτͷΈͳΒɺͦͷίϯϙʔωϯτͩ ͚࠶ϨϯμϦϯά ίϯϙʔωϯτʹดͨ͡σʔλͷมߋݕग़ Root (new Vue)
• ίϯϙʔωϯτͷ data/props ͕ɺଞͷࢠίϯϙʔω ϯτʹόέπϦϨʔ͍ͯ͠Δ߹ɺόέπϦϨʔઌͷ ࢠίϯϙʔωϯτ࠶ϨϯμϦϯά σʔλͷ୯ํόΠϯσΟϯά pass props Root
(new Vue)
• ࢠίϯϙʔωϯτ͕ίϯϙʔωϯτͱv-modelͰํ σʔλόΠϯσΟϯά͍ͯ͠Δ߹ɺʹσʔλม ߋΛ௨ͨ͠߹ɺࢠ࠶ϨϯμϦϯά͢Δ v-modelʹΑΔํόΠϯσΟϯά pass props emit events Root
(new Vue)
ଞͷ ϨϯμϦϯάγεςϜ ͱͷൺֱ
Demystifying Frontend Framework Performance • Nodric.js 2016 Ͱൃදͨ͠ Evan You
ࢯͷ্هλΠτϧͷࢿ ྉ͕Α͘·ͱ·͍ͬͯΔͷͰɺҰݟ͢Δ͜ͱΛ͓קΊ͢ Δ • εϥΠυ https://docs.google.com/presentation/d/ 1Ju5NryLLI-2aXm_XwsdF5rU0QpOpeyVW9F8JeeSuj-k/ edit#slide=id.p • ϏσΦ https://www.youtube.com/watch?v=Ag-1wmHWwS4
͞ΒͳΔ࠷దԽʹ͚ͯ
࠷దԽ߲ • ςϯϓϨʔτͷࣄલίϯύΠϧ + ϥϯλΠϜ • v-for ʹΑΔϦετϨϯμϦϯά࣌ʹ͓͚Δ key ଐੑ
• render ؔʹΑΔύϑΥʔϚϯενϡʔχϯά࣮ • ؔܕίϯϙʔωϯτ (functional component)
αʔόαΠυϨϯμϦϯά
αʔόαΠυϨϯμϦϯάͷॏཁੑ • γϯάϧϖʔδΞϓϦέʔγϣϯʹΑΓϢʔβʔʹ৺ ྑ͍UXΛఏڙͰ͖ΔΑ͏ʹͳͬͨ • ͕ɺҎԼͷ͕͋Δ • SEOରࡦ • ॳظදࣔͷϩʔσΟϯά࣌ؒ
ैདྷͷVueͰ͔ͬͨ͠ • Vue 2.0 ҎલͷόʔδϣϯͰɺυΩϡϝϯτϑϥάϝϯ τʹΑΔੜDOMϕʔεʹΑΔϨϯμϦϯάͷͨΊɺίΞ ଆͰαϙʔτ͍ͯ͠ͳ͔ͬͨ • ରԠ͢ΔʹɺαʔυϕϯμͷϨϯμϥΛར༻͢Δ͔ɺ ಠࣗʹରԠ͢Δඞཁ͕͋ͬͨ
• ͞ΒʹɺϋΠυϨʔγϣϯͷΈɺΫϥΠΞϯταΠ υͷϥΠϒϥϦͷαʔόαΠυରԠͳͲɺશ෦ಠࣗʹର Ԡ͢Δඞཁ͕͋ͬͨ
Vue 2.0 Ͱڥ͕େ͖͘վળ • Virtual DOM ʹΑΓநԽ͞ΕͨϨϯμϦϯά • ϋΠυϨʔγϣϯͷΈ •
ϢχόʔαϧରԠ͞Εͨެࣜʹఏڙ͢ΔϥΠϒϥϦ • Node.js ͚ʹϨϯμϥ • ίϯςΩετʹΑΔႈੑΛอূ͢ΔϨϯμϦϯά • αʔό͚ʹ࠷దԽ͞ΕͨϞδϡʔϧͷόϯυϦϯά
• Vue 2.0 Ҏ߱ͰɺҎԼͷެࣜϥΠϒϥϦͱόϯυϦϯ άπʔϧͰɺαʔόαΠυϨϯμϦϯάΛ༰қʹ࣮ݱ͢ Δ͜ͱ͕Ͱ͖Δ ͬͨαʔόαΠυϨϯμϦϯάڥ vue vuex vue-router
vue-server-renderer Vue library stack + and other …
Vue 2.0 ʹ͓͚Δ αʔόαΠυϨϯμϦϯά ͷྲྀΕʹ͍ͭͯݟ͍͖ͯ·͠ΐ͏ʂ
Client Server αʔόଆͰɺVueͰ࡞ΒΕͨ ΞϓϦέʔγϣϯίʔυ͕ αʔόଆͰಈ࡞͢ΔΑ͏όϯ υϧԽ͞Εͨͷ͕ಡΈࠐ·Ε ͯಈ࡞ ίϯϙʔωϯτΛαʔόଆ ͰϨϯμϦϯά͢ΔͨΊͷ ϨϯμϥΠϯελϯεԽ
Client Server ϦΫΤετ GET /:user_id/profile ϢʔβʔʹΑΔΞΫηε ɾɾɾ ϦΫΤετຖʹ αϯυϘοΫεΛ࡞ͯ͠ ίϯςΩετΛࢦఆͯ͠
ΤϯτϦϙΠϯτͷ Vue Λ࣮ߦͤ͞Δ
Client Server route ʹରԠ͢Δ ίϯϙʔωϯτΛ ಈ࡞ͤ͞ɺσʔλ ΛϑΣον ɾɾɾ ΫϥΠΞϯτʹ͢ॳظঢ়ଶΛJSONͱ͠ ͯຒΊࠐΉΑ͏ʹ
HTMLϨϯμϦϯάͯ͠ϨεϙϯεΛฦ͢
Client Server Ϩεϙϯε Ϛϯτ&ϋΠυϨʔτ ΫϥΠΞϯταʔό͔ΒϨϯμϦϯά͞Ε ͨHTMLͱΫϥΠΞϯτ͚ʹόϯυϧԽ͞ ΕͨΞϓϦέʔγϣϯίʔυ͕৴͞ΕΔ ϒϥβଆͰHTMLΛϩʔυͨ͠ ޙɺΤϯτϦϙΠϯτͱͳΔVueΛ ಈ࡞ͤ͞ɺϚϯτ&ϋΠυϨʔτ
ʹΑ࣮ͬͯࡍʹ༰Λදࣔ͢Δ
Client Server ޙɺΫϥΠΞϯτଆͰ ϧʔςΟϯάΛߦ͍ɺ ඞཁʹԠͯ͡σʔλΛϑΣονͯ͠ ϨϯμϦϯά͢Δ
αʔόαΠυϨϯμϦϯάΛߏ͢Δ4ཁૉ • 1. Ϩϯμϥ • 2. ϋΠυϨʔγϣϯ • 3. ίϯςΩετ
• 4. όϯυϦϯά
1. Ϩϯμϥ
ఏڙ͢ΔϨϯμϥ • Vue ΞϓϦέʔγϣϯΛαʔόαΠυͰϨϯμϦϯά͢ ΔͨΊͷ Node.js ڥͰಈ࡞͢ΔϨϯμϦϯάϞδϡʔ ϧ • Ϩϯμϥɺࢦఆ͞Εͨ
Vue ΞϓϦέʔγϣϯͷrender ؔʹΑͬͯऔಘͨ͠ԾϊʔυπϦʔΛ walk ͯ͠ HTML จࣈྻͱͯ͠ϨϯμϦϯά
جຊతͳ͍ํ • NPMͰΠϯετʔϧ $ npm install vue-server-renderer • ϨϯμϥΛ࡞ͯ͠ɺrenderToString ͰϨϯμϦϯά
const Vue = require('vue') const renderer = require('vue-server-renderer').createRenderer() const vm = new Vue({ render (h) { return h('div', 'hello') } }) renderer.renderToString(vm, (err, html) => { console.log(html) // -> <div server-rendered="true">hello</div> })
Stream ʹΑΔϨϯμϦϯά • Node.js ͷ Stream API αϙʔτɻrenderToStream Ͱ stream
Λ࡞ͯ͠ɺstream ΠϯλʔϑΣΠεʹΑͬͯϊ ϯϒϩοΩϯάͳϨϯμϦϯά͕Մೳ app.get('/', (req, res) => { const vm = new App({ url: req.url }) const stream = renderer.renderToStream(vm) res.write(`<!DOCTYPE html><html><head><title>...</title></head><body>`) stream.on('data', chunk => { res.write(chunk) }) stream.on('end', () => { res.end('</body></html>') }) })
ΩϟγϡʹΑΔߴԽ • Φϓγϣϯʹ cache ΦϒδΣΫτΛࢦఆ͢Δ͜ͱͰɺί ϯϙʔωϯτͷඳը݁ՌΛΩϟογϯά͢Δ͜ͱʹΑ Γɺ͞ΒͳΔύϑΥʔϚϯεΛ্͕Մೳ const LRU =
require('lru-cache') const renderer = createRenderer({ cache: LRU({ max: 10000 }) })
Ωϟγϡʹ͓͚ΔඞࢸΠϯλʔϑΣΠε • Φϓγϣϯʹ ࢦఆ͢Δ cache ΦϒδΣΫτɺҎԼͷ ΠϯλʔϑΣΠεΛ࣮͍ͯ͠ΕΩϟογϡՄೳ { get: (key:
string, [cb: Function]) => string | void, set: (key: string, val: string) => void, has?: (key: string, [cb: Function]) => boolean | void // optional }
ྫ: RedisClient ʹΑΔΩϟογϡ const redisClient = require('redis').createClient() const renderer =
createRenderer({ cache: { get: (key, cb) => { redisClient.get(key, (err, res) => { // handle error if any cb(res) }) }, set: (key, val) => { redisClient.set(key, val) } } })
ίϯϙʔωϯτͷΩϟογϡ • ίϯϙʔωϯτΛΩϟογϡʹରԠ͢ΔͨΊʹɺҎ ԼΛ༰Λ options ʹؚΊ࣮ͯ͢Δඞཁ͕͋Δ • name ΦϓγϣϯʹΑΔϢχʔΫͳίϯϙʔωϯτ໊ •
serverCacheKey ؔʹΑͬͯίϯϙʔωϯτຖʹϢ χʔΫͳΩʔΛฦ͢
ྫ: ίϯϙʔωϯτͷΩϟογϡରԠ export default { name: 'item', // required props:
['item'], serverCacheKey: props => props.item.id, render (h) { return h('div', this.item.id) } }
ΩϟγϡʹΑΔ෭࡞༻ • ࣍ͷΑ͏ͳؒҧ͍ͬͨํΛ͢ΔͱϨϯμϦϯάपΓͷ όάΛੜΈग़͢ͷͰҙ͕ඞཁ • άϩʔόϧঢ়ଶʹґଘ͢ΔࢠίϯϙʔωϯτͷΩϟο γϡ • εϩοτΛड͚͚ΔίϯϙʔωϯτͷΩϟογϡ
ΩϟογϡͷϕετϓϥΫςΟε • ಉ͡ props Λड͚औͬͨΒඞͣಉ͡HTMLΛඳը͢Δί ϯϙʔωϯτʹରͯ͠Ωϟογϡ͢Δͷ͕ཧ • ੩తͳHTMLΛඳը͢Δίϯϙʔωϯτ • ϦετͷΞΠςϜΛඳը͢Δίϯϙʔωϯτ
• ϘλϯɺΞϥʔτͳͲͷҰൠతͳ UI ίϯϙʔωϯτ
2. ϋΠυϨʔγϣϯ
ݱ࣮ੈքͷϋΠυϨʔγϣϯ • ϋΠυϨʔγϣϯӳޠͰhydrationɻݕࡧ͢Δͱ ʮϦΞॅͷੈքʯͰɺओʹҎԼͷΑ͏ͳҙຯ • hydration: ਫิڅ • ҎԼิ •
dehydration: ਫ • re-hydration: ิਫ
Vue ͷੈքͰʁ • ࡶʹ͍͏ͱʮhydration: ঢ়ଶิڅʯ • DOM ͕ঢ়ଶ (JSON) Λิڅͯ͠
ظ͖࢟͢ʹͳΔ͜ͱ DOM
• αʔόαΠυͰϨϯμϦϯά͞ΕͨDOMπϦʔͱɺΫϥΠΞϯτα ΠυͰߏங͞ΕΔԾϊʔυπϦʔ͕Ұக͢Δ͔Ͳ͏͔ͷݕূͱ ࠶ߏங·ͰͷҰ࿈ͷࣄͷ͜ͱ ۩ମతʹϋΠυϨʔγϣϯͬͯʁ HTTP App (Root vue instance)
Render Functions ... <div id="app" server-rendered="true"> ... </div> ... response hydrate JSON new Vue({ … }).$mount(‘#app’) render Virtual Node Tree Rendered DOM Tree window.__initial__ = { …. } checking set $el
• αʔόαΠυͰϨϯμϦϯά͞ΕͨDOMπϦʔΛഁغͯ͠ɺΫϥΠ ΞϯταΠυͰैདྷͷVirtual DOM ͷॲཧϑϩʔͰϨϯμϦϯά αʔόͱΫϥΠΞϯτ͕Ұக͠ͳ͔ͬͨ߹ HTTP App (Root vue
instance) Render Functions ... <div id="app" server-rendered="true"> ... </div> ... response hydrate JSON new Vue({ … }).$mount(‘#app’) Rendered DOM Tree window.__initial__ = { …. } checking render Virtual Node Tree diff + patch
։ൃϞʔυͱϓϩμΫγϣϯϞʔυͷҧ͍ • ։ൃϞʔυͰɺΫϥΠΞϯτଆͰੜ͞ΕͨԾϊʔ υπϦʔͷɺαʔόͰϨϯμϦϯά͞ΕͨDOMπϦʔͱ Ұக͍ͯ͠Δ͔Ͳ͏͔ͷݕূॲཧ࣮ߦ͢Δ • ϓϩμΫγϣϯϞʔυͰɺύϑΥʔϚϯεΛ࠷େԽ͢ ΔͨΊʹ͜ͷݕূΛແޮʹ͍ͯ͠Δ
ϋΠυϨʔγϣϯͷҙࣄ߲ • αʔόαΠυϨϯμϦϯάͱΫϥΠΞϯτͷϋΠυϨʔ γϣϯʹ͓͍ͯɺValid ͳߏΛͨ͠ HTML Λهࡌ͠ͳ ͍ͱෆҰக͕ൃੜ͢Δ <table> <tr><td>hi
evan!</td></tr> </table> ϒϥβࣗಈతʹ <tbody>Λૠೖ͢Δ VueςϯϓϨʔτΛίϯύΠϧ͢Δͱ ͦͷߏͷ·· Virtual-DOM Λੜ͢Δ ෆਖ਼ͳ<table>ߏΛ࣋ͬͨ ςϯϓϨʔτ
3. ίϯςΩετ
• VueͰ࡞ͨ͠ΞϓϦέʔγϣϯίʔυ͕Πϯελϯε Խ͞ΕΔͱɺNode ϓϩηεʹ͓͍ͯΫϥΠΞϯτ͔Β དྷͨϦΫΤετؒͰڞ༗͞ΕΔ ίϯςΩετͷඞཁੑ request1 request2 requestX άϩʔόϧͳঢ়ଶ͕Ԛછ͞
Εͯ͠·͍ɺϨϯμϦϯά ݁ՌʹѱӨڹΛٴ΅͢
ίϯςΩετʹΑΔԚછͷճආ • ֤ϦΫΤετຖʹίϯςΩετΛ࡞ͯ͠ɺͦ͜ʹঢ়ଶ Λઃఆ͢Δ͜ͱͰɺ֤αϯυϘοΫεͰঢ়ଶ͕อޢ ͞ΕΔ request1 request2 requestX Context Context
Context vm. runInNewContext vm. runInNewContext vm. runInNewContext
ίϯςΩετͷಋೖ • αʔό͚ͷ Vue ΞϓϦέʔγϣϯΛ࣮ߦͤ͞ΔΤϯτ ϦϙΠϯτΛ༻ҙ͢Δ // server-entry.js import Vue
from 'vue' import App from './App.vue' const app = new Vue(App) // όϯυϧϨϯμϥଆͰϨϯμϦϯάͰݺͼग़͢ࡍʹ͞ΕΔ // ίϯςΩετΛड͚औΔؔΛ export ͢Δ // ؔ Vue ΠϯελϯεΛฦ͢ඞཁ͕͋Δ export default context => { // σʔλͷϑΣον return app.fetchServerData(context.url).then(() => { return app }) }
ίϯςΩετͷಋೖ • Vue ͕ఏڙ͢ΔόϯυϧϨϯμϥʹΑΓίϯςΩετʹ ରԠͨ͠ϨϯμϥΛ࡞ const createBundleRenderer = require('vue-server-renderer').createBundleRenderer const
bundle = require('./dist/server-bundle.js') const rederer = createBundleRenderer(bundle)
ίϯςΩετͷಋೖ • ϦΫΤετϋϯυϥͰίϯςΩετΛ࡞͠ɺϨϯμ ϥͷϨϯμϦϯάϝιουʹࢦఆ࣮ͯ͠ߦ // for express app.get('*', (req, res)
=> { const context = { url: req.url } const renderStream = renderer.renderToStream(context) res.write(html.head) renderStream.on('data', chunk => { ... res.write(chunk) }) renderStream.on('end', () => { res.end(html.tail) }) }) ϨϯμϦϯάͷࡍʹ༻ҙͨ͠Τ ϯτϦϙΠϯτ͕ݺΕͯɺί ϯςΩετ͕͞ΕΔ
4. όϯυϦϯά
αʔό͚όϯυϧԽ͢Δඞཁੑ • ϦΫΤετຖͷαϯυϘοΫεɺΞϓϦέʔγϣϯ ίʔυΛಈ࡞ͤ͞ΔͱશͯͷґଘϞδϡʔϧҰॹʹҾ ͖ࠐΜͰಈ࡞͢Δ Node process request request load
load ಡΈࠐ·Εͨ ґଘϞδϡʔ ϧΛɺ࠶ղ ੳ͠ɺධՁ͢ ΔͨΊɺύ ϑΥʔϚϯε Α͘ͳ͍
ґଘϞδϡʔϧΛ֎෦Խ͢Δ͜ͱͰղܾ • ґଘϞδϡʔϧΛ֎෦Խ͢Δ͜ͱʹΑΓɺαϯυϘοΫ ε্ʹɺNodeϓϩηεͰಡΈࠐ·ΕͨґଘϞδϡʔ ϧͰࢀরͯ͠ಈ࡞͢ΔΑ͏ʹͳΔ Node process request request reference
αʔό͚ͷόϯυϧԽ (webpackͷྫ) • αʔό͚ʹɺґଘϞδϡʔϧΛ֎෦Խ͢ΔΑ͏ʹόϯ υϧԽͷઃఆΛ͢Δ // webpack ͷઃఆ module.exports =
{ entry: './src/server-entry.js', target: 'node', output: { ..., libraryTarget: 'commonjs2' }, // webpack ͷ externals Λར༻ͯ͠ɺpackage.json ͷ "dependencies"ͷ ݩʹ // ͋ΔϞδϡʔϧΛશͯ֎෦Խ externals: Object.keys(require('./package.json').dependencies) }
αʔό͚ͷόϯυϧԽ • αʔόଆͰґଘϞδϡʔϧ͕֎෦Խ͞ΕͨόϯυϧϑΝ ΠϧΛҾ͖ࠐΈɺόϯυϧϨϯμϥͰͦΕΛࢦఆͯ͠Ϩ ϯμϥΛ࡞͢Δ const createBundleRenderer = require('vue-server-renderer').createBundleRenderer const
bundle = require('./dist/server-bundle.js') const rederer = createBundleRenderer(bundle)
αʔό͚όϯυϧԽͷҙࣄ߲ • ϦΫΤετؒͰڞ༗͞Ε͍ͯΔͷͰɺґଘϞδϡʔϧ͕ ႈ͔Ͳ͏͔֬ೝ͕ඞཁ • ҟͳΔϦΫΤετؒͰৗʹಉ͡ඳը݁ՌʹͳΔͷ͔ • ґଘϞδϡʔϧʹάϩʔόϧͳঢ়ଶΛ͍࣋ͬͯͯɺΞϓ ϦέʔγϣϯίʔυʹΑͬͯมߋ͞ΕΔͷ͍ͭͯ ҙ͕ඞཁ
αʔόαΠυϨϯμϦϯά ʹ͓͚Δ όϯυϧԽͷΠϝʔδ
όϯυϧԽͷΠϝʔδ(webpack)
αʔόαΠυʹ͓͚Δ Vue ͷ API
ϥΠϑαΠΫϧϑοΫ • beforeCreate • created
• beforeMount • mounted • beforeUpdate • updated • activated • deactivated • beforeDestroy • destroyed ݺΕΔϑοΫ ݺΕͳ͍ϑοΫ
ϥΠϑαΠΫϧϑοΫҎ֎ • Ͳͷ API ͕αʔόαΠυͰಈ࡞͢Δ͔Ͳ͏͔ɺݫີʹ ॻ͔Ε͍ͯͳ͍ • DOM ʹґଘ͍ͯ͠ͳ͍ܥ౷ͷͷɺಈ࡞͢Δͣ •
Πϕϯτ: $emit, $off, $on, $once • σʔλܥ: $watch, $set, $delete, $data, ͳͲ • ͦͷଞΖΖ …
αʔόαΠυϨϯμϦϯά Example
vue-hackernews-2.0 • ެࣜͰఏڙ͢ΔαʔόαΠυϨϯμϦϯά example https://github.com/vuejs/vue-hackernews-2.0
·ͱΊ
ϨϯμϦϯάγεςϜ • Vue ͷϨϯμϦϯάγεςϜɺ࠷దԽͱϦΞΫςΟϒ γεςϜͱΈ߹ΘͤΔ͜ͱʹΑΓɺಛʹҙࣝ͠ͳͯ͘ ͍ϨϯμϦϯάΛఏڙ͍ͯ͠Δ • ·ͨɺҙͰϨϯμϦϯάΛ੍ޚͰ͖ΔΈΛఏڙ͢ Δ͜ͱͰɺ͞ΒͳΔύϑΥʔϚϯεͷ্Մೳʹͳͬ ͍ͯΔ
αʔόαΠυϨϯμϦϯά • ެࣜʹఏڙ͢ΔϥΠϒϥϦͱπʔϧʹΑͬͯɺαʔόα ΠυϨϯμϦϯάΛ༰қʹ࣮ݱ͢Δ͜ͱ͕Մೳʹͳͬͨ • ͜ΕʹΑΓɺαʔόαΠυͱΫϥΠΞϯταΠυͷ྆ํ ʹରԠՄೳͳɺuniversal/isomorphic ͳΞϓϦέʔγϣ ϯߏங͕Մೳʹͳͬͨ
ͱ͍͏Θ͚Ͱɺ
universal / isomorphic ͳ ίϯϙʔωϯτΛ࡞Δʹʁ
༩͑ΒΕͨσʔλʹରͯ͠ ৗʹಉ͡ϨϯμϦϯά݁ՌʹͳΔ ؔܕͳίϯϙʔωϯτΛ ࣮Λ͢Δ͜ͱʂ
Ҏ্ʂ
one more thing …
Vue.js ຊޠެࣜαΠτ Coming soon!! Vue.js ຊޠެࣜαΠτ
ຊਓ͚ Vue.js ίϛϡχςΟ • URL https://vuejs-jp-slackin.herokuapp.com • Vue.js ຊޠެࣜαΠτܦ༝ͰࢀՃͰ͖·͢ http://jp.vuejs.org
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ