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.2k
vuejs-meetup
Railsで作られたサービスにVue.jsを導入したというお話
kazupon
January 29, 2015
Tweet
Share
More Decks by kazupon
See All by kazupon
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.4k
Reactivity Transform
kazupon
1
1.3k
わたしのOSS活動
kazupon
1
990
Vue with Vite
kazupon
2
2.5k
State of Vue I18n
kazupon
3
550
まちにまった Vue.js 3
kazupon
14
5.5k
Other Decks in Programming
See All in Programming
Ethereum_.pdf
nekomatu
0
460
どうして僕の作ったクラスが手続き型と言われなきゃいけないんですか
akikogoto
1
120
TypeScriptでライブラリとの依存を限定的にする方法
tutinoko
2
660
初めてDefinitelyTypedにPRを出した話
syumai
0
400
Kaigi on Rails 2024 〜運営の裏側〜
krpk1900
1
190
3 Effective Rules for Using Signals in Angular
manfredsteyer
PRO
0
110
Content Security Policy入門 セキュリティ設定と 違反レポートのはじめ方 / Introduction to Content Security Policy Getting Started with Security Configuration and Violation Reporting
uskey512
1
520
アジャイルを支えるテストアーキテクチャ設計/Test Architecting for Agile
goyoki
9
3.3k
Laravel や Symfony で手っ取り早く OpenAPI のドキュメントを作成する
azuki
1
110
型付き API リクエストを実現するいくつかの手法とその選択 / Typed API Request
euxn23
8
2.2k
弊社の「意識チョット低いアーキテクチャ」10選
texmeijin
5
24k
Jakarta Concurrencyによる並行処理プログラミングの始め方 (JJUG CCC 2024 Fall)
tnagao7
1
290
Featured
See All Featured
Producing Creativity
orderedlist
PRO
341
39k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
27
840
Designing for humans not robots
tammielis
250
25k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
246
1.3M
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
10
720
Reflections from 52 weeks, 52 projects
jeffersonlam
346
20k
The World Runs on Bad Software
bkeepers
PRO
65
11k
Testing 201, or: Great Expectations
jmmastey
38
7.1k
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 Έ͍ͨͷͰɺಈ͔ ͯ͠ϨϯμϦϯά͍ͨ͠
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ