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
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
1.8k
Oxlint JS plugins
kazupon
1
1.4k
gunshi
kazupon
1
260
Nitro v3
kazupon
2
600
わたしのOSS活動
kazupon
3
650
Vapor Revolution
kazupon
3
4.3k
Vue.js最新動向
kazupon
3
1.7k
Vue 3.4
kazupon
13
4.9k
Vue & Vite Rustify
kazupon
4
2.5k
Other Decks in Programming
See All in Programming
Verilogで学ぶCPU自作入門.pdf
uyuki234
6
3.2k
Agents on Rails - Rails at Scale 2026
irinanazarova
0
210
The Rails Doctrine Decade
koic
2
380
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
520
AI活用は、個人から組織へ|マルチプレイヤーエージェントハーネス「QM」の社内活用事例 / AI use is moving from individuals to orgs
rkaga
1
210
AWSに止められる覚悟してますか?
morizo_1984
1
280
難しいけど、読めた。- OSSの入口に立った話。
sts11142
0
120
Building an Out-of-Order CPU
latte72
1
800
Are APIs Still Relevant in the AI Era?
soyuka
0
310
ハーネス設計入門 〜 基礎知識の整理から実務へのステップアップ 〜
kinopeee
17
15k
技術的負債を組織課題として解く-増えすぎたマイクロサービスとの戦い-
reimaru
1
2.3k
すこし踏み込む CancellationToken
htkym
2
1k
Featured
See All Featured
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
950
Designing Dashboards & Data Visualisations in Web Apps
destraynor
232
55k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.1k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
370
How to Ace a Technical Interview
jacobian
281
24k
Believing is Seeing
oripsolob
1
220
Accessibility Awareness
sabderemane
1
220
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
570
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
610
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
1
540
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
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
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ