写真は私の登壇の様子を参加していた別の社員に撮っていただいたものです。
1. はじめに
FVGの永井優斗です。秋のブログ週間1本目です。
2025年10月25日に開催された「Vue Fes Japan 2025」のライトニングトーク(LT)に登壇いたしました。昨年のプラチナスポンサーセッション(昨年の登壇報告記事) に続き、2年連続での登壇となります。
今回フューチャーアーキテクト株式会社は、今回ゴールドスポンサーおよびルームネイミングライツスポンサーとして協賛しました。ネーミングライツにより、1つの部屋(トラック)は「経営とITをデザインする フューチャーアーキテクトトラック」として会場内でアナウンスされたほか、#vuefes_future のハッシュタグで本トラックの公演内容はX上でポストされました。
『React Nativeならぬ”Vue Native”が実現するかも? 新世代マルチプラットフォーム開発フレームワークの LynxとLynxのVue.js対応を追ってみよう』 というタイトルで、今年3月にOSS化されたばかりのフレームワーク「Lynx」と、そのVue.js対応の動向についてお話ししました。
また、登壇とは別の時間は、私は当日ボランティアスタッフとしても活動しており、ハンズオンの出欠を確認したりしておりました。
2. CfP(Call for Proposal)の採択
今回はスポンサーセッションではなく、公募(いわゆる「CfPを通す」)での登壇です。VueFesでは、CfPのスピーカーの採択は、完全に匿名で応募者の名前を隠したうえで登壇タイトルと詳細をみて判断をしているそうです。その話を聞いて、著名なスピーカーでなくとも、ニッチな話をもっていけば、もしかしたら面白がって採択されるかもな?と思い、ニッチなネタとして3月のLynx公開時からLynxのVue対応のネタを温めていました。
また、私はTSKaigi 2025にも運営スタッフとして参加しています。そのAfter Eventで知り合ったsiminoriさんと、「(上記のような)Lynxネタを温めており、VueFesのLTに応募をしようと思っている」と話したところ、意気投合し、お互いLTでCfPに応募しようと盛り上がりました。まさか、二人ともLT登壇が決まるとは思わず、スピーカーの紹介がWebサイトに載った時には大変驚きました。
来年はTSKaigiの登壇…かな…!
3. 登壇内容と資料
当日の登壇資料はこちらです。
LTなので詳細を語ることを省きつつも、Vueでモバイル開発することへの過去のチャレンジを振り返りつつ、3月発表されたLynxに着目していること、Vue対応の経緯と現状について話しました。
また、VueLynxについては海外の方によってまとめられた記事があるため、自分がこれを発表する意味はなんだろうと考えた結果、自分自身がLynxを触った感想も入れ込みました。それを入れようとおもったがゆえに、9月にLynx記事を仕込んだりするわけですが笑
4. 当日の様子と反響
昨年に続き、2年連続での登壇、3年連続の参加となりましたが、やはりVue Fes Japanの熱気は格別でした。
毎回思うことですが、”Fes”という言葉が正しいと思えるくらいにお祭り感のある、たのしいカンファレンスでした。
登壇後の反応としては、Lynxそのものを知らなかったという話や、LynxとLynxのVue対応、あれだけ盛り上がってたのにいまは確かにそうでもないなといったコメントをいただきました。
<X上での反応>
また、自己紹介スライドにあった、BBQインストラクターやパエリア検定上級が気になったと声をかけてくださった人もいました笑
スポンサーブース
フューチャーアーキテクトは、今回ゴールドスポンサーとしてブースも出展していました。
今回は配布物(ノベルティ)を3種類用意し、くじ引きでどれかが当たる企画を実施しました。1等は最近発売された当社の渋川よしきが著者の1人である「JavaScript/TypeScript実力強化書――関数・非同期処理・型システム完全攻略」に渋川のサイン入りでお渡ししました。
くじはVueFesだからと、Vue.jsを使ったHTMLファイルで作成しました。(GeminiさんがHTMLファイル1枚で9割方作成してくれました。生成されるまで30秒くらい。ほんとにすごいね。)
実際には一瞬で抽選処理は終わるんですが、ドキドキ感のある「抽選中」の演出をつけたものを作成しました。
くじのソースコードはこちら
vuefeslot.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>くじ引き</title> <script src="https://unpkg.com/vue@3"></script> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background-color: #f4f7f9; } #app { text-align: center; background-color: white; padding: 30px 40px; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); width: 90%; max-width: 400px; } h1 { color: #333; margin-bottom: 10px; } button { padding: 15px 35px; font-size: 1.2em; font-weight: bold; cursor: pointer; border: none; background: linear-gradient(45deg, #ff8a00, #e52e71); color: white; border-radius: 8px; transition: transform 0.2s, box-shadow 0.2s; margin: 25px 0; box-shadow: 0 4px 15px rgba(229, 46, 113, 0.4); } button:disabled { background: #ccc; cursor: not-allowed; box-shadow: none; } button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(229, 46, 113, 0.5); } #result { margin-top: 20px; font-size: 2em; font-weight: bold; color: #e52e71; height: 60px; display: flex; justify-content: center; align-items: center; } .finished-message { color: #555; font-weight: bold; } .prize-list { list-style: none; padding: 0; margin-top: 25px; text-align: left; } .prize-list li { margin-bottom: 10px; font-size: 1.1em; background-color: #f9f9f9; padding: 10px; border-radius: 5px; }
@keyframes drumroll { 0% { transform: scale(1); opacity: 0.7; } 25% { transform: scale(1.1); opacity: 1; } 50% { transform: scale(1); opacity: 0.7; } 75% { transform: scale(1.1); opacity: 1; } 100% { transform: scale(1); opacity: 0.7; } }
.drawing-text { font-size: 1.5em; color: #555; animation: drumroll 1.2s infinite; } </style> </head> <body>
<div id="app"> <h1>🥢Future Architectくじ🥢</h1> <button @click="startDraw" :disabled="isFinished || isLoading">くじを引く</button>
<div id="result"> <p v-if="isLoading" class="drawing-text">抽選中...</p> <p v-else-if="result">{{ result }}</p> </div>
<div v-if="isFinished" class="finished-message"> <p>くじはすべてなくなりました!</p> </div>
<hr>
<h2>残り本数</h2> <ul class="prize-list"> <li v-for="prize in prizes" :key="prize.name"> {{ prize.name }}: 残り <strong>{{ prize.stock }}</strong> 本 </li> </ul> </div>
<script> const { createApp, ref, computed } = Vue
createApp({ setup() { const prizes = ref([ { name: '🥇 1等', stock: 5 }, { name: '🥈 2等', stock: 60 }, { name: '🥉 3等', stock: 100 }, ]);
const result = ref(''); const isLoading = ref(false);
const totalStock = computed(() => { return prizes.value.reduce((total, prize) => total + prize.stock, 0); });
const isFinished = computed(() => totalStock.value === 0);
const startDraw = () => { if (isFinished.value || isLoading.value) return;
isLoading.value = true; result.value = '';
setTimeout(() => { drawLottery(); isLoading.value = false; }, 2500); };
const drawLottery = () => { const random = Math.floor(Math.random() * totalStock.value) + 1; let accumulated = 0; for (const prize of prizes.value) { if (prize.stock > 0) { accumulated += prize.stock; if (random <= accumulated) { result.value = `🎉 ${prize.name} 当選! 🎉`; prize.stock--; break; } } } };
return { prizes, result, startDraw, isFinished, isLoading }; } }).mount('#app'); </script>
</body> </html>
|
ありがたいことに、昨年を大幅に上回る人数の方がスポンサーブースに立ち寄っていただいたこともあり、ノベルティは13時頃にはすべてはけてしまったようです。
それ以降に立ち寄ってくださった皆様、申し訳ございません。
5. 最後に
今回のセッションでお話しした「VueLynx」は、まだプロトタイプが公開された段階で、正直なところ、この先どうなるかは分かりませんし、Lynxそのものも、まだ日本語での情報が少ない状況です。登壇資料の最後に書いた通り、本登壇を通じて、実現した時のワクワク感が参加者の皆さんに届き、少しでも盛り上がれば幸いです。
最後になりましたが、このような素晴らしい技術コミュニティの「お祭り」を企画・運営をし、LT登壇の機会をいただきました、Vue.js日本ユーザーグループの皆様、そして当日、LTにご参加いただいた皆様、ありがとうございました。