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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
690
Oxlint JS plugins
kazupon
1
1.3k
gunshi
kazupon
1
230
Nitro v3
kazupon
2
550
わたしのOSS活動
kazupon
3
620
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
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
410
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
4
1.4k
AI時代のUIはどこへ行く?その2!
yusukebe
21
7.2k
Lemonade + Foundry Toolkit でお手軽アプリ開発
seosoft
1
340
フロントエンドとバックエンドで「1文字」を揃えよう
youkidearitai
PRO
0
700
AIで効率化できた業務・日常
ochtum
0
140
Snowflake Summitでの新機能 CoCo / CoWork / snowflake-summit-2026-overall-what-new-coco
tatsuhiro
1
140
New "Type" system on PicoRuby
pocke
1
950
ふつうのFeature Flag実践入門
irof
7
4k
AIとASP.NET Coreで雑Webアプリを作った話
mayuki
0
650
技術記事、AIに書かせるか、自分で書くか? 〜それでも私が自分の手で書く理由〜 / #QiitaConference
jnchito
2
1.4k
生成AI時代にこそ効くGo | Why Go Works in the Age of Generative AI
mom0tomo
8
3.2k
Featured
See All Featured
The Curse of the Amulet
leimatthew05
1
13k
Making Projects Easy
brettharned
120
6.7k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
390
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.5k
A designer walks into a library…
pauljervisheath
211
24k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
730
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
140
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.6k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
250
Done Done
chrislema
186
16k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
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
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ