Pro Yearly is on sale from $80 to $50! »

昔懐かしいインターネットの風物詩を最近の技術で作る話

D8becf939cfb6d977394016667eca429?s=47 Makoto Henmi
September 01, 2018

 昔懐かしいインターネットの風物詩を最近の技術で作る話

アクセスカウンター、一言掲示板など昔のインターネットにあったものがどんどん失われて行っています。
そんな過去の遺物たちをVue.jsとFirebaseを使って作った話をしました。

サイト: https://teijigo-beer-ti.me/
ソースコード: https://github.com/makowis/teijigo-beer-time

D8becf939cfb6d977394016667eca429?s=128

Makoto Henmi

September 01, 2018
Tweet

Transcript

  1. ੲջ͔͍͠Πϯλʔωοτͷ෩෺ࢻΛ ࠷ۙͷٕज़Ͱ࡞Δ࿩ 2018/09/01(౔) ߹ಉษڧձ in େ౎ձԬࢁ -2018 Summer- ҳݟ੣ (@mako_wis)

  2. About Me ҳݟɹ੣ʢϔϯϛɹϚίτʣ T witter: @mako_wis GitHub: makowis גࣜձࣾΫϨΦϑʔΨ Engineering

    Manager Okayama.rb ΠϕϯτཱͯΔ܎ɺதࠃ஍ํDBษڧձ Ԭࢁελοϑ झຯɿΨϯϓϥ੡࡞ɺϓϩάϥϛϯά
  3. None
  4. ԻָܥͷձࣾͳͷͰ

  5. Իָ࡞ΕΔਓ͕ଟ͍

  6. M3ʹࣾ಺ͷϝϯόʔͰ ԻָCDΛग़͢Β͍͠

  7. ๻͸Իָ࡞Εͳ͍ͷͰ

  8. ϗʔϜϖʔδ࡞ͬͯΈͨ

  9. ϓϩτλΠϓ ๭ΞχϝͬΆ͍ϩΰ จࣈΛ఺໓ͤͨ͞ΓεΫ ϩʔϧͤͨ͞Γ͍ͯ͠ ੲջ͔͍͠ײ͡ʹ͔ͨͬ͠ ͨ

  10. ࢖ΘΕͯΔݹͷ෩෺ࢻ blinkͱmarquee ϗʔϜϖʔδͷ૷০ͱ͍͑ ͹͜Ε λάͱͯ͠͸ഇࢭ CSSΞχϝʔγϣϯͰ࣮૷ ಈ࡞ݟ͍ͨਓ͸ʮblink htmlʯ΍ʮmarquee htmlʯ ͰάάΖ͏

  11. blinkΛcss animationͰ࣮૷ @keyframes blink { 75% { opacity: 0; }

    } .blink { animation: blink 1s step-end infinite; }
  12. ͜Ε͕ҙ֎ͱࣾ಺Ͱ ΢έͯ͠·ͬͨ

  13. ϓϩτλΠϓ৭ʑ໰୊ ͋Γͦ͏ͳͷͰ࡞Γ௚ͨ͠

  14. ίϯηϓτ ʮੲջ͔͍͠ײ͡ʯ

  15. None
  16. https://teijigo-beer-ti.me/

  17. ։ൃϝϯόʔ

  18. ։ൃϝϯόʔ

  19. ։ൃϝϯόʔ ࣾ௕ ଞࣾͷਓ ଞࣾͷਓ

  20. https://github.com/ makowis/teijigo-beer-time

  21. ݹͷ෩෺ࢻ ΞΫηεΧ΢ϯλʔ Ұݴܝࣔ൘ ϑϨʔϜͬΆ͍σβΠϯ എܠʹը૾Λϕλ഑ஔ

  22. ߏ੒ αΠτɿVue.js + T ypescript αʔόʔɿFirebase Hosting σʔλϕʔεɿFirebase Realtime Database

  23. ջ͔͍͠ݟͨ໨Ͱ͕͢ ʮγϯάϧϖʔδΞϓϦέʔγϣϯʯ Ͱ͢

  24. ΞΫηεΧ΢ϯλʔ τοϓϖʔδʹΞΫηεͰ Χ΢ϯτΞοϓ ϦϩʔυͰ΋Χ΢ϯτΞο ϓ ഉଞॲཧ͸ಛʹߟྀ͠ͳ ͍ ϑΝΠϧʹΧ΢ϯτ਺࣋ͬ ͯͨΞϨͳײ͡ͷ࣮૷

  25. ࣮૷ Χ΢ϯτͷอଘʹ͸Firebase Realtime DatabaseΛ࢖༻

  26. Firebase Realtime Database NoSQLσʔλϕʔε σʔλ͸JSONܗࣜͰอଘ͞ΕΔ σʔλ͸઀ଓ͞Ε͍ͯΔΫϥΠΞϯτͱϦΞ ϧλΠϜʹಉظ͞ΕΔ

  27. None
  28. Firebase Realtime Databaseଆͷ४උ

  29. Firebase΁ͷొ࿥͸ Ͱ͖͍ͯΔલఏͰ͍͖·͢

  30. ίϯιʔϧͷDatabase͔Β Realtime DatabaseΛ࡞੒

  31. ϩοΫϞʔυͰ։࢝ʢޙͰઃఆม͑·͢ʣ

  32. access_counterͷϑΟʔϧυΛ௥Ճ͢Δ

  33. ϧʔϧʹaccess_counter΁ͷΞΫηεݖΛઃఆ

  34. ઀ଓ৘ใͷ֬ೝ

  35. ઃఆը໘ͷ ΢ΣϒΞϓϦʹFirebaseΛ௥ՃΛΫϦοΫ

  36. ઀ଓʹ࢖༻͢Δ৘ใΛ֬ೝͯ͠ϝϞ͓ͯ͘͠

  37. Vue.jsଆͷ࣮૷

  38. firebaseͷϥΠϒϥϦΛ௥Ճ // npmͷ৔߹ $ npm install firebase —save // yarnͷ৔߹

    $ yarn add firebase
  39. firebase-configΛ௥Ճ // src/firebase-config.js import firebase from 'firebase/app'; import 'firebase/database'; const

    config = { // ؅ཧը໘Ͱ֬ೝͨ͠databaseURLΛઃఆ͢Δ databaseURL: "https://teijigo-beer-time.firebaseio.com", }; firebase.initializeApp(config); // ࠓճ͸database͚ͩ࢖༻͢ΔͷͰdatabaseΛexport export default firebase.database();
  40. ը໘ଆͷ࣮૷ <template> <div> <h1>TOP</h1> <p>͋ͳͨ͸{{ access }}ਓ໨ͷ๚໰ऀͰ͢ɻ</p> </div> </template> <script>

    import firebase from 'firebase'; import database from '@/firebase-config'; export default { name: 'Top', data() { return { access: 0, }; }, created() { this.countUp(); }, methods: { countUp() { database .ref('access_counter') .once('value') .then((snapshot: firebase.database.DataSnapshot | null) => { if (snapshot) { // σʔλϕʔε͔Βऔಘͨ͠஋ʹ+1Λ͢Δ const access = parseInt(snapshot.val(), 10) + 1; // σʔλϕʔεͷ஋Λߋ৽͢Δ database.ref().update({ access_counter: access }); // ը໘දࣔ༻ͷม਺ʹ֨ೲ this.access = access; } }); }, }, }; </script>
  41. Χ΢ϯτΞοϓͷॲཧ methods: { countUp() { database .ref('access_counter') .once('value') .then((snapshot: firebase.database.DataSnapshot

    | null) => { if (snapshot) { // σʔλϕʔε͔Βऔಘͨ͠஋ʹ+1Λ͢Δ const access = parseInt(snapshot.val(), 10) + 1; // σʔλϕʔεͷ஋Λߋ৽͢Δ database.ref().update({ access_counter: access }); // ը໘දࣔ༻ͷม਺ʹ֨ೲ this.access = access; } }); }, },
  42. ը໘΁ͷදࣔ <template> <div> <h1>TOP</h1> <p>͋ͳͨ͸{{ access }}ਓ໨ͷ๚໰ऀͰ͢ɻ</p> </div> </template> <script>

    import firebase from 'firebase'; import database from '@/firebase-config'; export default { name: 'Top', data() { return { access: 0, }; }, created() { this.countUp(); },
  43. ΞΫηεΧ΢ϯλʔ׬੒

  44. ؆୯Ͱ͢Ͷ

  45. Ұݴܝࣔ൘ ΩϦ൪ใࠂ౳ʹ࢖͏ܝࣔ ൘ ϦΞϧλΠ Ϝߋ৽͕؆୯ͩͬ ͨͷͰ͍࣮ͭ૷ ੲͷܝࣔ൘ͱҧͬͯϦϩʔ υඞཁ͋Γ·ͤΜ

  46. ࣮૷ ίϝϯτͷอଘʹ͸Firebase Realtime DatabaseΛ࢖༻

  47. Realtime Databaseଆͷ ઃఆ

  48. ϧʔϧʹmessagesΛ௥Ճ͠·͢

  49. Vue.jsଆͷ࣮૷

  50. ը໘΁ͷදࣔ <template> <div> <h1>Ұݴܝࣔ൘</h1> <div class="message-form"> <div class="message-form-group"> <label for="nameInput">HN(ϋϯυϧωʔϜ)</label>

    <input type="text" id="nameInput" maxlength="20" v-model="name"> </div> <div class="message-form-group"> <label for="messageInput">ϝοηʔδ</label> <input type="text" id="messageInput" maxlength="100" v-model="message"> </div> <button type="button" @click="sendMessage">ૹ৴</button> </div> <div> <ul class="message-list"> <li v-for="(item, key) in messageList" v-bind:key="key"> <p> {{item.message}} by {{item.name}} </p> <p class="time-label"> {{item.createdAt}} </p> </li> </ul> </div> </div> </template> <script> import firebase from 'firebase'; import database from '@/firebase-config'; export default { name: 'Top', data() { return { name: '', message: '', messageList: [], }; }, created() { this.listen(); }, methods: { listen() { database .ref('messages/') .on('value', (snapshot: firebase.database.DataSnapshot | null) => { if (snapshot) { const list = snapshot.val(); const keys = Object.keys(list); const values = keys.map((v) => list[v]); this.messageList = values.sort((a: Message, b: Message) => { if (a.sortKey > b.sortKey) return 1; if (a.sortKey < b.sortKey) return -1; return 0; }); } }); }, sendMessage() { if (!this.name || !this.message) return; const message = { name: this.name, message: this.message, createdAt: moment(new Date()).format('YYYY/MM/DD H:mm:ss'), sortKey: -new Date(), }; database.ref('messages/').push(message); this.name = ''; this.message = ''; } }, }; </script>
  51. ίϝϯτͷૹ৴ <template> <div class="message-form"> <div class="message-form-group"> <label for="nameInput">HN(ϋϯυϧωʔϜ)</label> <input type="text"

    id="nameInput" maxlength="20" v-model="name"> </div> <div class="message-form-group"> <label for="messageInput">ϝοηʔδ</label> <input type="text" id="messageInput" maxlength="100" v-model="message"> </div> <button type="button" @click="sendMessage">ૹ৴</button> </div> </template> <script> methods: { sendMessage() { if (!this.name || !this.message) return; const message = { name: this.name, message: this.message, createdAt: moment(new Date()).format('YYYY/MM/DD H:mm:ss'), sortKey: -new Date(), }; // σʔλϕʔεʹίϝϯτΛ௥Ճ database.ref('messages/').push(message); this.name = ''; this.message = ''; } }, }; </script>
  52. ίϝϯτͷૹ৴ <script> methods: { sendMessage() { if (!this.name || !this.message)

    return; const message = { name: this.name, message: this.message, createdAt: moment(new Date()).format('YYYY/MM/DD H:mm:ss'), sortKey: -new Date(), }; // σʔλϕʔεʹίϝϯτΛ௥Ճ database.ref('messages/').push(message); this.name = ''; this.message = ''; } }, }; </script>
  53. ίϝϯτͷදࣔ listen() { database .ref('messages/') .on('value', (snapshot: firebase.database.DataSnapshot | null)

    => { if (snapshot) { const list = snapshot.val(); const keys = Object.keys(list); const values = keys.map((v) => list[v]); this.messageList = values.sort((a: Message, b: Message) => { if (a.sortKey > b.sortKey) return 1; if (a.sortKey < b.sortKey) return -1; return 0; }); } }); },
  54. ίϝϯτͷදࣔ <template> <div> <ul class="message-list"> <li v-for="(item, key) in messageList"

    v-bind:key="key"> <p> {{item.message}} by {{item.name}} </p> <p class="time-label"> {{item.createdAt}} </p> </li> </ul> </div> </template> <script> import firebase from 'firebase'; import database from '@/firebase-config'; export default { name: 'Top', data() { return { name: '', message: '', messageList: [], }; }, created() { this.listen();
  55. Ұݴܝࣔ൘׬੒

  56. ؆୯Ͱ͢Ͷ

  57. ·ͱΊ ੲͷջ͔͍͠΋ͷΛ࠷ۙͷٕज़Ͱ࠶࣮૷ͯ͠Έ· ͨ͠ ୯७ͳϞϊͳͷͰࢼ͠ʹ࡞ͬͯΈΔ୊ࡐͱͯ͠͸ ஸ౓͍͍ Firebase͜Ε͘Β͍ͷϞϊͰ͋Ε͹ແྉͰ࢖͑· ͢ Έͳ͞Μ΋࠶࣮૷ͯ͠Έ·͠ΐ͏

  58. ͓·͚

  59. ϑϨʔϜͬΆ͍σβΠϯ ͬΆ͍σβΠϯͳͷͰϑ ϨʔϜ͸࢖͍ͬͯͳ͍ CSSͰͦΕͬΆ͘ݟͤͯ ·͢ ͍ͭ͜มܗ͠·͢

  60. ϑϨʔϜͷมܗ ύιίϯ εϚʔτϑΥϯ

  61. φ΢໊͍લΛ ߟ͑ͯ͘Εͨਓ͕͍·ͨ͠

  62. None
  63. #ϨεϙϯγϒϑϨʔϜ