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
vuejs-meetup
Search
kazupon
January 29, 2015
Programming
16
8.3k
vuejs-meetup
Railsで作られたサービスにVue.jsを導入したというお話
kazupon
January 29, 2015
Tweet
Share
More Decks by kazupon
See All by kazupon
Vapor Revolution
kazupon
2
2.7k
Vue.js最新動向
kazupon
3
1.5k
Vue 3.4
kazupon
13
4.5k
Vue & Vite Rustify
kazupon
4
2.1k
Vue.jsエコシステム動向2023
kazupon
17
7.5k
Reactivity Transform
kazupon
1
1.3k
わたしのOSS活動
kazupon
1
1k
Vue with Vite
kazupon
2
2.5k
State of Vue I18n
kazupon
3
560
Other Decks in Programming
See All in Programming
PHPで作るWebSocketサーバー ~リアクティブなアプリケーションを知るために~ / WebSocket Server in PHP - To know reactive applications
seike460
PRO
2
510
fs2-io を試してたらバグを見つけて直した話
chencmd
0
240
CSC305 Lecture 26
javiergs
PRO
0
140
命名をリントする
chiroruxx
1
420
PHPUnitしか使ってこなかった 一般PHPerがPestに乗り換えた実録
mashirou1234
0
230
テストコードのガイドライン 〜作成から運用まで〜
riku929hr
5
730
Effective Signals in Angular 19+: Rules and Helpers @ngbe2024
manfredsteyer
PRO
0
140
【re:Growth 2024】 Aurora DSQL をちゃんと話します!
maroon1st
0
780
SymfonyCon Vienna 2025: Twig, still relevant in 2025?
fabpot
3
1.2k
PSR-15 はあなたのための ものではない? - phpcon2024
myamagishi
0
140
MCP with Cloudflare Workers
yusukebe
2
220
Scalaから始めるOpenFeature入門 / Scalaわいわい勉強会 #4
arthur1
1
340
Featured
See All Featured
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
8
1.2k
Building Better People: How to give real-time feedback that sticks.
wjessup
365
19k
How to Think Like a Performance Engineer
csswizardry
22
1.2k
Docker and Python
trallard
42
3.1k
Documentation Writing (for coders)
carmenintech
66
4.5k
Music & Morning Musume
bryan
46
6.2k
Statistics for Hackers
jakevdp
796
220k
Testing 201, or: Great Expectations
jmmastey
40
7.1k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
Become a Pro
speakerdeck
PRO
26
5k
It's Worth the Effort
3n
183
28k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
0
98
Transcript
RailsͰ࡞ΒΕͨαʔϏεʹ Vue.jsΛಋೖͨ͠ͱ͍͏͓ Vue.js Meetup 2015-01-28 @kazupon
About Me • @kazupon • ॴଐɿCUUSOO SYSTEM • ׂɿϦʔυΤϯδχΞ •
ࣄɿ ϑϩϯτΤϯυɺόοΫΤϯυɺΠϯϑϥɺͳͲɺγεςϜ શൠɺ΄΅ϑϧελοΫʂ • ࡞ͬͨVue.jsͷPluginɿ vue-i18n: https://github.com/kazupon/vue-i18n vue-validator: https://github.com/kazupon/vue-validator
Vue.jsΛ͍ͬͯΔฐࣾαʔϏε
Vue.jsಋೖલͷΞʔΩςΫνϟ αʔό ΫϥΠΞϯτ
• αʔόαΠυͰHTMLΛϨϯμϦϯά͢Δయܕతͳ ϖʔδભҠͷΞϓϦέγϣϯ • UX͕ٻΊΒΕΔ෦ɺjQueryͷϓϥάΠϯɺAjax ͳͲͰରԠ • JavascriptɺCSS Rails
ͷ Sprockets ʹ͓·͔ͤ
͔͠͠ɺ͕ɻɻɻ
Կ͕ͳͷ͔ • ΫϥΠΞϯταΠυಛʹ MVC ͳͲͷΞϓϦέʔ γϣϯΞʔΩςΫνϟ͕ͳ͘ɺยखؒΦϨΦϨΞʔ ΩςΫνϟͰΧΦεʹͳ͍ͬͯΔ • ഭΓདྷΔϏδωεཁ݅ͰɺUI/UXվળʹΑΔߴͳ UIΛཁٻ͢Δ࣮͕૿͖͑ͯͯɺϝϯς͕ͭΒͨ
Μɻɻɻ
Rails & data-*Λͬͨ ΦϨΦϨController # Javascript $(document).ready(function () { var
module = constantnize($(‘body’).data(‘controller-name’)); if (module) { module.init(); } }); # HTML template <html> ... <body data-controller-name=“<%= contoller.controller_name %>”>...</doby> ... </html>
jQueryΠϕϯτϋϯυϥ & DOMૢ࡞ͷཛྷ $('body .projects').on('focus', 'textarea', function () { var
value = $(this).val().split(‘\n’); var value_row = 0; $.each(value, function(i, val) { value_row += Math.max(Math.ceil(val.length / self.cols), 1); }); var input_row = $(this).attr('rows'); var original_row = $(this).data('default_row'); var next_row = (input_row <= value_row) ? value_row + 0 : Math.max(value_row + 1, original_row); $(this).attr('rows', next_row); });
͜ͷ··ͰϠόΠʂ Ϋιίʔυ͕૿͑Δ͔Γʂʂ
͜ΕҎ্ϠόΫͳΒͳ͍Α͏Α͏ Կͱ͔ͤͶʂ
ͱ͏͍͏Θ͚ͰVue.jsΛಋೖ http://vuejs.org
ͳͥɺVue.jsΛಋೖͨ͠ͷ͔ʁ
લఏ݅ • طଘͷjQueryͰॻ͔Εͨίʔυࢿ࢈Λ׆༻Ͱ͖Δ͜ͱ • ݱঢ়ͷΞϓϦέʔγϣϯʹରͯ͠ΈࠐΈ༰қͰɺط ଘ࣮ʹରͯ͠෭࡞༻͕΄ͱΜͲͳ͍͜ͱ • ίϩίϩUI༷͕มΘΔϏδωεཁ݅ʹରͯ͠ɺ͜Ε ·ͰͲ͓Γɺ։ൃεϐʔυΛҡ࣋Ͱ͖Δ͜ͱ •
ଞͷϓϩδΣΫτར༻ՄೳͰ͋Δ͜ͱ • ͙͢ʹ͑Δ͜ͱ
ͱ͍͏Θ͚ͰҎԼΛݕ౼
Vue.jsΛಋೖͨ͠ཧ༝ • ଞͷϥΠϒϥϦͱׯব͕΄ͱΜͲͳ͍ • σʔλόΠϯσΟϯάͰɺੜDOMૢ࡞ͷ࣮Λͦ ΜͳʹΨϦΨϦॻ͔ͣͱɺUIͷදࣔɾৼ͍Λָ ʹ࣮Ͱ͖Δ • CommonJS ϕʔεͰϞδϡʔϧԽͰ͖Δ
• ֶशίετ͕͍
Vue.jsΛಋೖͨ͠ݱঢ়
ViewModel • Vue.jsͷWebαΠτʹॻ͍ͯ͋ Δͱ͓ΓɺVue.jsͷϞδϡʔϧ ͱͯ͠ར༻Ͱ͖ΔΑ͏ɺ Vue.extendͰαϒΫϥεԽ͠ ͯɺmodule.exports ͢Δ component system:
http://vuejs.org/guide/components.html var Modal = module.exports = Vue.extend({ template: require('./modal.html'), components: { ... }, data: function () { return { title: '', ... } }, methods: { show: function () { ... }, hide: function () { ... }, onClickClose: function (e) { ... } } });
ViewModelͷొ • Vue.extendͰϞδϡʔϧԽ͠ ͨͷɺVue.component Ͱ άϩʔόϧొͤͣɺɺ ඞཁͳͱ͖ʹ require ͯ͠ componentsΦϓγϣϯͰࢦఆ
• ޙ v-component ɺv-ref Ͱ ΨγΨγ͏ # ViewModel var Widget = module.exports = Vue.extend({ template: require('./widget.html'), ... components: { modal: require('../modal') }, ... methods: { onClickOpenModal: function () { this.$.modal.show(); }, ... } }); # View <div class=“widget”> ... <div v-component="modal" v-ref="modal"></div> ... </div>
ૄ݁߹ͳViewModel • TODOϦετͷΑ͏ͳɺࢠؔ ͕͋ΔViewModelΛ࡞͢ Δ߹ɺΠϕϯτAPIΛۦ • Πϕϯτ໊conflict͕ى͖ͳ ͍Α͏namespaceΛར༻ • v0.11.4Ͱɺv-eventsΛ͑
ɺࢠଆͰ$emitͰൃՐͨ͠Π ϕϯτर͑·͢ … events: { ‘ChildWidget:remove’: function (vm, index) { // remove the item from the item collection // … }) } … onClickRemoveItem: function (event, index) { this.$dispatch( ‘ChildWidget:remove’, event.targetVM, index ); }) onClickRemoveItem: function (event, index) { this.$dispatch( ‘ChildWidget:remove’, event.targetVM, index ); }) ࢠ1 ࢠx ϢʔβʔͷΠϯλ ϥΫγϣϯʹΑͬͯ আཁٻ͕དྷͨΒɺΠϕϯτΛdispatch͠ ͯɺଆͰআͯ͠Β͏
Template • View ʹ૬͢Δ Template(html) ɺrequire Ͱ͖Δ Α͏ɺͳΔ͘ϑΝΠϧͱͯ͠ཧ # Tempalte
<div class=“widget”> ... <div v-component="modal" v-ref="modal"></div> ... </div>
Filter • FilterViewModelͷ࣌ͱಉ ༷ɺmodule.exportsͯ͠ɺ requireͰ͖ΔΑ͏ʹ͍ͯ͠Δ • FilterΛొ͢Δͱ͖ɺ ComponentԽͨ͠ViewModel ͷͱ͖ͱಉ༷ɺVue.filterͰά ϩʔόϧొͤͣɺඞཁͳͱ
͖ʹrequireͯ͠ɺfiltersΦϓ γϣϯʹࢦఆ͍ͯ͠Δ # Filter module module.exports = { required: function required (val) { return !val ? false : true; }, ... }; # ViewModel var filters = require(‘./filters’); var Invitation = module.exports = Vue.extend({ template: require('./invitaion'), ... filters: { validateRequired: function (val) { this.validation.email = filters.required(val); return val; }, … } });
Resource • ಛʹpluginతͳͷΛ͏͜ͱ ͳ͘ɺϥΠϒϥϦ(superagent) ΛͬͯɺWebAPIͷend-point ʹϦΫΤετ͢Δ͜ͱͰ resourceʹΞΫηε superagent: http://visionmedia.github.io/superagent/ var
request = require('superagent-browserify'); module.exports = list; function list (params, fn) { params = params || {}; var endPoint = ‘/api/v1/xxx’; // ... request .get(endPoint) .query(params) .withCredentials() .end(fn); }
Plugin • vue-i18n: ࠃࡍԽରԠ • αʔόଆͰ cookie ʹઃఆͨ͠ locale ใΛऔಘͯ͠ɺදࣔॲཧ
͢Δݴޠͷઃఆ • v-t ·ͨ Vue.t Ͱ Rails ͷ i18n like ʹ key Λࢦఆͯ͠ར༻ # Entrypoint var cookie = require('cookie-cutter'); var Vue = require('vue'); var i18n = require('vue-i18n'); // locale setting var locale = cookie.get('locale') || ‘en’; Vue.use(i18n, { lang: locale, locales: { en: require('./locales/en.json'), ja: require('./locales/ja.json') } }); # Tempalte <div> <p v-t=“foo.bar"></p> </div> vue-i18n: https://github.com/kazupon/vue-i18n
Ϟδϡʔϧཧํ๏ • Vue.jsͰॻ͍ͨͷར ༻͢Δͷɺapp/ assets/javascriptsͱ ผʹઐ༻σΟϨΫτϦ Λ࡞ͬͯͦ͜ͷதͰ ཧ . !""
Capfile !"" Gemfile !"" Gemfile.lock !"" README.md !"" Rakefile !"" app !"" config !"" config.ru !"" db !"" frontend !"" lib !"" log !"" public !"" script !"" spec #"" vendor ͜ͷσΟϨΫτϦ Ͱཧ
Ϟδϡʔϧߏ • ಠࣗنͰߏԽ • ԼهͷΑ͏ʹɺVue.jsͰॻ͍ͨ Ϟδϡʔϧ͚ͩͰͳ͘ɺଞͷ ϞδϡʔϧҰॹʹཧ - ڞ௨ϥΠϒϥϦ -
UIؔ࿈ - APIΫϥΠΞϯτ - ଟݴޠϦιʔε . !"" Makefile !"" gulpfile.js !"" index.js !"" lib $ !"" api $ ... $ !"" utils.js $ !"" validates.js $ #"" widgets !"" locales $ !"" en.json $ #"" ja.json #"" package.json
ϞδϡʔϧͷϏϧυ • Gulp + BrowserifyͰϞδϡʔ ϧԽͨ͠JSίʔυ܊ͨͪΛɺ ·ΔͬͱϏϧυͯ͠όϯυϧ • ϏϧυʹΑΓόϯυϧͨ͠JS ϑΝΠϧΛɺapp/assets/
javascripts ʹஔͤͯ͞ɺ application.jsͰrequireͤ͞Δ • ࠷ऴతʹSprocketsͰ·͔ͤΔ . !"" app $ !"" assets $ $ !"" images $ $ !"" javascripts $ $ $ !"" application.js $ $ $ !"" bundle.js $ $ $ ... $ $ $ #"" zzz.js $ $ #"" stylesheets !"" frontend $ !"" Makefile $ !"" gulpfile.js $ !"" index.js $ !"" lib $ $ !"" api $ $ ... $ $ !"" utils.js $ $ !"" validates.js $ $ #"" widgets $ !"" locales $ $ !"" en.json $ $ #"" ja.json $ #"" package.json ...
ϨΨγʔίʔυͷΈࠐΈ • RailsͷControllerͰϨϯμϦ ϯά͞ΕΔςϯϓϨʔτϑΝ ΠϧʹscriptλάͰɺrequire Ͱ·Δͬͱ࣮ͨ͠ ViewModelΛҾ͖ࠐΜͰɺ ΠϯελϯεԽ͢Δ͚ͩ • ॳظσʔλ͕ඞཁͳΒɺ
JSONΛϨϯμϦϯάͯ͠ dataʹηοτ # erb template ... <div id="widgets" v-cloak v-repeat="item: items" v-component="widget-item"></div> ... <script> (function () { var Widget = require('bundle').Widget; var data = JSON.parse('<%= j(object.to_json).html_safe %>'); new Widget({ el: '#widgets', data: data }); })();
Vue.jsΛಋೖͨ݁͠Ռ • ࣮ͨ͠ComponentΛɺ؆୯ʹ ͍·ΘΓͨ͠Γɺ֦ுͨ͠Γ͢Δ ͜ͱ͕Ͱ͖ΔΑ͏ʹͳͬͨ - ։ൃੜ࢈ੑ্ - ϝϯςφϯεੑ্ Vue.jsͰ࣮ͨ͠
Component
Vue.jsΛಋೖͨ݁͠Ռ • ଞͷϓϩδΣΫτͰɺϥΠϒϥϦײ֮Ͱɺ ComponentΛҾ͖ࠐΜͰར༻Ͱ͖ΔΑ͏ͳͬͨ - Ϟδϡʔϧੑ্ Vue.jsͰ࣮ͨ͠ ComponentΛ֦ு
·ͱΊ • jQueryͷΫιίʔυ͕૿৩͢ΔΧΦεͳΫϥΠΞϯτ αΠυ • զʑͷχʔζʹ͋ͬͨVue.jsΛ࠾༻͢Δ͜ͱͰɺΫϥ ΠΞϯταΠυΛߏԽ • ϨΨγʔίʔυΛ͋·Γमਖ਼͢Δ͜ͱͳ͘ΈࠐΊΔ ͜ͱ͕Ͱ͖ɺ͔ͭଞͷϓϩδΣΫτͰར༻Մೳ
• ͜Ε·ͰͲ͓Γ։ൃεϐʔυΛҡ࣋ͭͭ͠ɺΧΦεͩͬ ͨੈքΛվળ͢Δ͜ͱ͕Ͱ͖ͨ
͓·͚
Vue.jsʹର͢Δෆຬ • UIͷঢ়ଶͱModelͷ͕ͬͪ͝Όʹͳ͍ͬͯΔɺ $data Λ͍ͨ͠ • αʔόαΠυͰ Vue.render Έ͍ͨͷͰɺಈ͔ ͯ͠ϨϯμϦϯά͍ͨ͠
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ