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
わたしのOSS活動
kazupon
2
360
Vapor Revolution
kazupon
3
3.2k
Vue.js最新動向
kazupon
3
1.5k
Vue 3.4
kazupon
13
4.6k
Vue & Vite Rustify
kazupon
4
2.2k
Vue.jsエコシステム動向2023
kazupon
17
7.6k
Reactivity Transform
kazupon
1
1.3k
わたしのOSS活動
kazupon
1
1k
Vue with Vite
kazupon
2
2.5k
Other Decks in Programming
See All in Programming
.NET Frameworkでも汎用ホストが使いたい!
tomokusaba
0
200
5分で理解する SOLID 原則 #phpcon_nagoya
shogogg
1
380
なぜイベント駆動が必要なのか - CQRS/ESで解く複雑系システムの課題 -
j5ik2o
14
4.7k
Datadog DBMでなにができる? JDDUG Meetup#7
nealle
0
150
pylint custom ruleで始めるレビュー自動化
shogoujiie
0
160
Datadog Workflow Automation で圧倒的価値提供
showwin
1
270
もう僕は OpenAPI を書きたくない
sgash708
6
1.9k
コードを読んで理解するko build
bells17
1
110
PRレビューのお供にDanger
stoticdev
1
240
ABEMA iOS 大規模プロジェクトにおける段階的な技術刷新 / ABEMA iOS Technology Upgrade
akkyie
1
220
推しメソッドsource_locationのしくみを探る - はじめてRubyのコードを読んでみた
nobu09
2
340
Ça bouge du côté des animations CSS !
goetter
2
150
Featured
See All Featured
GraphQLの誤解/rethinking-graphql
sonatard
69
10k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
Designing for humans not robots
tammielis
250
25k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
29
1k
Code Reviewing Like a Champion
maltzj
521
39k
Optimizing for Happiness
mojombo
377
70k
The Straight Up "How To Draw Better" Workshop
denniskardys
232
140k
Code Review Best Practice
trishagee
67
18k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
193
16k
What's in a price? How to price your products and services
michaelherold
244
12k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
27
1.9k
Product Roadmaps are Hard
iamctodd
PRO
51
11k
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 Έ͍ͨͷͰɺಈ͔ ͯ͠ϨϯμϦϯά͍ͨ͠
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ