Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
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
2.8k
Oxlint JS plugins
kazupon
1
1.4k
gunshi
kazupon
1
280
Nitro v3
kazupon
2
630
わたしのOSS活動
kazupon
3
660
Vapor Revolution
kazupon
3
4.4k
Vue.js最新動向
kazupon
3
1.7k
Vue 3.4
kazupon
13
5k
Vue & Vite Rustify
kazupon
4
2.5k
Other Decks in Programming
See All in Programming
全ての永続トークンを、生まれる前に消し去りたい!
sylph01
2
1.6k
No More NoSuchMethodError
exoego
1
120
Kotlinの15年とServer-Side Kotlin
n_takehata
1
170
AI主導のプログラミング学習アプリを作ってみた
econanringo
0
200
AIで開発が速くなった先で、開発チームは何を目指すか
shihochan
1
1.9k
Snowflake Openflow Connector利用の勘所 / 20261024developersio2026tokyo-snowflake-openflow
kasacchiful
PRO
0
250
オーケストレーション記述言語 Katari
yukikurage
0
210
Zero-Tap Sign-In対応、結局何をすればいいの?
mitohato14
0
1.1k
ActionController meets Protocol Buffers
sorah
1
750
Inertia Rails: The One-Person Framework with React & Friends
skryukov
0
200
AIが生成したコードは読むべきか?抽象化設計とコードの品質保証 / abstraction-design-and-quality
minodriven
12
7.7k
Performance Optimization With LLMs
xleitix
0
230
Featured
See All Featured
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
310
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.9k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
340
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
1
470
The Curious Case for Waylosing
cassininazir
1
590
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
560
Bash Introduction
62gerente
615
220k
Agile that works and the tools we love
rasmusluckow
331
22k
The browser strikes back
jonoalderson
0
1.9k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
How to make the Groovebox
asonas
2
2.5k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.6k
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
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ