20220109のvue.jsに関する記事は7件です。

Vue - The Complete Guide (Udemy) 12章・13章フォームとリクエストについての要約!

教材 Vue - The Complete Guide (w/ Router, Vuex, Composition API) 理解を深めるために簡単なメモを作成しました。早くしっかりと実装できるようになりたい! session 12 フォーム 140 入力されたデータを取得する方法は、主に2つ! 独自のカスタム入力イベントリスナーを作成する方法 v-modelを使用する方法。 141 入力するデータの型を指定する方法! v-モデルを使用を使用すると、型の指定画できる。  デフォルトのJavaScriiptの検証画面に表示されるものは、すべて文字列として表示されるようである。しかし、Vueを使えば、簡単に型指定ができて後でDBを使用する時に簡単に操作画できる。 142 vモデルとドロップダウン 143ラジオボックスとラジオバタンでのvモデルの使用 typeで任意の形に表示を変更する。 あたり前だが、入力したあとはnullに戻す。そうしないと前のデーが残ってしまうみたい。 配列にしてデータを保存しないと全部選択されてしまう![]で指定して上げれば良い。 valueの値は項目ごとに作成しないといけない。 グループで管理されている入力データ軍と単体で管理されている入力データ軍が存在している。 単体は、もともとを真偽で設定して置き、デフォルトの設定は偽にしておき、入力されると真になるように設計を行う! 144 フォームの検証の機 @blurと設定をすると、blurイベントは、要素がフォーカスを失ったときに発生します。このイベントと focusout との違いは、 focusout がバブリングを行うのに対し blur は行わないことです。 Element: blur イベント 新しくフォームを入力する場合は、メソッドを作成しそこに入力される値が正しいのかを検証する必要がある。 145 カスタムコンポーネントの実装 手順 新規コンポーネントの作成 親で新規コンポーネントをimportとcomponetしてあげる。 選択を強調したいのなら、新規クラスを作成して、色をつけるようなクラスを作成する。 他の値が入らないようにv-bindで正しい入力かどうかを判定して上げるようにする。 146カスタムコンポーネントで、v-modelを使用する方法! modelValueを使用して、カスタムコンポーネントに使用して同期する。ここの範囲は、とても難しいので一度では理解ができない。他の本などを読んで理解したら再度取り組んで見よう。 Session 13 httpリクエスト 149 ローカル環境での開発から離れるための概要の説明 151 Fiarebaseの説明 goggleのサービスで、バックエンドで行う機能を提供してくれるサービス。リアルタイムでデータ同期ができることがいいところ。 152 httpリクエストを送信方法について axiousのおすすめ サードパーティー製のパッケージのようだが、かなり使いやすいみたいである。httpリクエストを行ってくれる。 fetch();について。データを送受信するためのメソッド。ここにUrlを書けばいい。 153 データを保存するためのpostの送信 firebaseにデータを送信するための、書き方を説明 JSONデータとして、送信しないと行けない。 156 データの取得(Get)と応用データの変換 fetch()でデータの入力をとってくるように記述する。 response.okがokだったら.jsonデータを取得できるように記載する。 失敗に終わったら、エラー文を送信してくれるように処理書くようにする。 157 コンポーネントをマウントする時のデータを取得方法。 - ローカル環境をisLoading = flaseに 常に観ているのではなくてリクエストが来たら動くように改良を重ねる。その後にリセットを行う。 158 データなしの状態での書影 - if文を書いて、中身がなかったらどうするかの処理を記載するようにする。 160 ブラウザ側のエラーを解決する方法! console.log(error)という風に記載をすれば、errorがはけるようになる。 161 サーバーサイドのエラー文を読み解く方法
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# Blazorで作ったサイトをVue.jsで作り直してみた

タイトルの通りのことをやってみました。 結論から言うと、Blazorをやっているとvueの学習コストが下がるので「dotnetしかやったことないよ!」という人にはVueはお勧めできるかと思います。 Blazorで作った話はこちら↓ BlazorでSkclusive-UIを使った話 ページ:https://syamaz.github.io/website/ 今回作ったものはこちら↓ ページ:https://syamaz.github.io/website-vue/ 環境 Vue3 Vue-router@4 Vuetify 3.0.0-alpha Vite gh-pages vueコンポーネントとrazorコンポーネント コンポーネントにはスタイル、テンプレート(UI)、スクリプトが含まれるという点でvueコンポーネントとrazorコンポーネントはほぼ同じ役割を持たせることができます 以下、Materialデザインの「カード」を列挙するページについてのコンポーネントです。 vue コード <template> <v-container> <h1>Works</h1> <v-container v-for="app in apps" v-bind:key="app"> <v-card> <v-card-title>{{app.name}}</v-card-title> <v-card-media> <v-img :src="app.img"></v-img> </v-card-media> <v-card-text>{{ app.text }}</v-card-text> <v-card-subtitle>Platform {{ app.platform.join(", ") }}</v-card-subtitle> <v-card-subtitle>Status {{app.status}}</v-card-subtitle> <v-card-text></v-card-text> <v-divider></v-divider> <v-card-actions> <v-btn v-if="app.url != ''" :to="app.url"><span class="text-info">Read more</span></v-btn> <v-btn v-if="app.outerurl != ''" :href="app.outerurl"><span class="text-info">Read more</span></v-btn> </v-card-actions> </v-card> </v-container> </v-container> </template> <script setup> import routineTreeImg from "../assets/RoutineTree.png" const apps = [ { name: "RoutineTree", img: routineTreeImg, text: "Task management application that helps you accomplish your daily routine tasks.", platform: ["iOS"], status: "In Review", url: "", outerurl: "https://syamaz.github.io/RoutineTree/" }, { name: "My homepage", img: "", text: "This website.", platform: ["web"], status: "Released", url: "/", outerurl: "" } ] </script> <style> </style> Blazor コード @page "/works" @inject NavigationManager navman @using website.Components @using website.Pages.Works.Parts @using website.Pages.Works.Datas <style> .styled-linkbutton { @*縦並び*@ display: block; text-transform:none; } .styled-linkbutton:is(:hover) { text-decoration: underline; } </style> <PageTitle>Works - sYamaz</PageTitle> <Typography Variant="TypographyVariant.H5"> Works </Typography> @foreach (var item in Datas) { <Box Padding="2" Margin="2"> <WorkCardView WorkData="@item" /> </Box> } @code { private IEnumerable<AnyWorkData> Datas { get { yield return new AnyWorkData { Title = "RoutineTree", Description = "Task management application that helps you accomplish your daily routine tasks.", ReadMoreURL = "https://syamaz.github.io/RoutineTree/", ImagePath = "images/RoutineTree.png", Status = WorkStatus.inReview, SupportPlatform = SupportPlatform.iOS }; yield return new AnyWorkData { Title = "Some web app/service", Description = "My practice project using Vue and AWS", ReadMoreURL = "", ImagePath = "images/Noimage.png", Status = WorkStatus.underDevelop, SupportPlatform = SupportPlatform.web }; // --- end --- yield return new AnyWorkData { Title = "My homepage", Description = "This website.", ReadMoreURL = "", ImagePath = "images/Noimage.png", Status = WorkStatus.release, SupportPlatform = SupportPlatform.web }; } } } ルーティング Blazorの場合、デフォルトでルーティング機能が付いてます。 @page "/{url}"をコンポーネント内で宣言することで他ページから遷移することができます。 vueでvue-routerを使う場合はコンポーネントで宣言というよりは一括で宣言することになります。(ドキュメントを十分に読み込めてないだけで、別の方法があるかもしれません) vue コード import { createRouter, createWebHistory } from 'vue-router' // ページのコンポーネントをインポート import AboutVue from '../components/About.vue' import WorksVue from '../components/Works.vue' import PostsVue from '../components/Posts.vue' const routes = [ { path: '/', name: 'About', component: AboutVue }, { path: '/works', name: 'Works', component: WorksVue }, { path: '/posts', name: 'Posts', component: PostsVue }, ] const baseURL = import.meta.env.BASE_URL; console.log("base : " + baseURL) const router = createRouter({ history: createWebHistory(baseURL), routes }) export default router GitHub Pages 基本的に楽をしたいため、gh-pagesnpmパッケージを使用します。 vite.config.jsにデバッグ時と公開時でBaseURLをスイッチする定義を追加 // vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vuetify from '@vuetify/vite-plugin' import path from 'path' // https://vitejs.dev/config/ export default defineConfig({ // 略 resolve: { alias: { '@': path.resolve(__dirname, 'src'), }, }, // この一行を追加 '/{Githubリポジトリ名}'とか'/'とか base: process.env.NODE_ENV === 'production' ? '{公開時}' : '{デバッグ時}', // 略 }) このbaseプロパティをvue-routerで使用します import.meta.env.BASE_URL: {string} アプリが配信されているベース URL。これは base 設定オプション によって決まります。 //vue-router@4 const baseURL = import.meta.env.BASE_URL; const router = createRouter({ history: createWebHistory(baseURL), routes }) export default router まとめ dotnet開発者が→Webに手を広げていく際の一つの道が、「WinForm/WPF/UWP」→「Blazor」→「vue」なのかもしれません
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マークダウンをPushしたら、GitHub Pagesにブログができたら便利

冬休みの自由研究テーマ markdown で記事を作成して、github にプッシュしてそのままブログになったら便利かなと思いました。 昨年初めて触った Vue.js を利用した自由研究です。 成果物 出来上がったもの https://kllc.github.io/markdown-blog/ ソース一式 https://github.com/kllc/markdown-blog 使い方 GitHub Pages を、Source は root で設定してぜひ使ってみて下さい。 markdown で記事を作成して、github にプッシュする。 index.js にその記事の パス(src) とその他属性情報を書く。 必須入力は、src のみ、その他は任意 // index.js export default () => { const index = [ { src: "md/sample1.md", // ここだけ必須 date: "22.1.1", title: "Sample", text: "text text text", img: "https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png", topics: ["vuetify", "markdown"], author: { name: "k", avatar: "https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png", message: "vuetify sample. https://store.vuetifyjs.com/products/parallax-theme-free", }, }, { src:"md/sample2.md" // これだけでも動きます。 }, : ]; return index; }; 目指したこと 社内外の知識共有を、無料で簡単に VSCode だけ使って発信可能にする。 そのために、Github Pages を使う。 Zenn みたいなデザインにする。 研究で何をしたか(使った技術) Vue.js Vuetify ⇒  デザイン面が作りやすいので去年知った Vue js を利用 Marked ⇒  マークダウンを HTML に変換するライブラリ highlight.js ⇒   Code を VSCode のようにハイライト表示する Tocbot ⇒   自動的に目次をつける npm/yarn で導入する か CDN か 各 javascript ライブラリは、CDN で導入しています。 Vue.js の場合、他のライブラリは npm で導入しないと上手く動かないことがあるので、npm/yarn のほうがいいと思うのですが、GitHub Pages でホストする場合、ソースファイルがシンプルになる CDN のがいいかなと思いました。 全部 CDN で導入したので、以下のようになります。 <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/vs2015.min.css" /> <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.11.1/tocbot.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/marked@3.0.7/marked.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/vbnet.min.js"></script> 研究でわかったことや思ったこと Vue.js 昨年はじめて触ったんですが、Vue.js ってすごい。めっちゃ好きになりました。 React も Angular も触ったことないですが、Vue.js が簡単だと聞きます。 むかし、PHP か Perl かって議論では圧倒的に Perl のが偉いって感じだったように思いますが、結局今 PHP 案件のほうが多いのは、簡単な方が生き残るからだと思います。 でも、JQuery と仲が悪くて、喧嘩して JQuery を動けなくしちゃうのは良くない。 Vuetify 今回はじめて触りました。気に入りました。 Bootstrap みたいなグリッドシステムもいいし、v-card とかもいい感じ。 でも v-row と v-col で、サイド固定幅メイン可変幅にするやり方が分からなかった。 結局直接計算して指定。 <v-card elevation="2" class="pa-4 mb-2" :style="'width:' + side_width + ';'" ></v-card> side_width() { if (this.clientWidth > 1280 - 16) { return "310px"; } else { return "100%"; } }, Marked 今回の研究テーマはここがメインだけど、簡単すぎて拍子抜け。 コピペしたら動きました。もっと流行っても良さそう。 const res = await axios.get(this.blogprop.src); marked.setOptions({ breaks: true, highlight: function (code, lang) { return hljs.highlightAuto(code, [lang]).value; }, }); this.bloghtml = marked(res.data); highlight.js code のハイライト表示は最初やるつもりなかったけど、Zenn に合わせるならあったほうがいい。 でも結構ハマった。 レンダリングされたあとの HTML に対して Class をつけるような処理をするらしく、JQuery と同じく、Vue.js と相性が悪い。 今回知ったのは、DOM が更新されたあとに動く this.$nextTick もっと早く知ってればよかったなぁ。 https://qiita.com/miyauchoi/items/73fa77f65b88214d9dba でも、それだけでも上手く行かなかった。 highlight-js の読み込みは、以下のように書くといいらしい。 https://stackoverflow.com/questions/13094541/how-to-reinitialize-highlight-js watch: { bloghtml: function (val) { this.$nextTick(function () { hljs.initHighlighting.called = false; hljs.initHighlighting(); }); }, }, Tocbot ZennやQiita みたいに、目次を付けられる機能。 https://errormaker.blog.fc2.com/blog-entry-52.html こちらは思ったより簡単だったけど、ヘッダーがかぶさっちゃうので、目次をクリックして移動したときにそこがヘッダーに隠れてしまう。 CSS で解決するといいらしいですが、でもその代わり、margin-top と padding-top が使えなくなる。しょうがないか。 https://www.tam-tam.co.jp/tipsnote/html_css/post4776.html <style> .content h1, .content h2, .content h3 { margin-bottom: 16px; margin-top: -100px; padding-top: 100px; } 画像 Zenn みたいに、画像が設定されないとき、自動的に画像が設定されたら簡単でいいなと思って調べたら、Zenn は Twemoji という、twitter で使える emoji を使っているみたい。 この一覧を取得してランダムに設定すればいいかと思ったけど、同じ名前なら同じアバターが設定されるとより良いかなと思い、名前から計算される数値を作り、その数値で emoji を設定することにしました。 我ながら良いアイデア。コピペで簡単に動いた。 https://qiita.com/_shimizu/items/eb6834f255d76b1ed8cf https://stackoverflow.com/questions/61634063/generate-deterministic-hash-number-between-0-and-1-from-string image(text) { return this.emojis[ Math.floor(this.decimalHash(text) * this.emojis.length) ]; }, decimalHash(string) { let sum = 0; for (let i = 0; i < string.length; i++) sum += ((i + 1) * string.codePointAt(i)) / (1 << 8); return sum % 1; }, 広告 誰かに使ってもらう前提で、あとからでも変更可能な埋め込み広告エリアを設定してみた。 ad.js を変更すれば、いつでも埋め込まれた html を変更できる。 <span v-html="ad"></span> import ad from "https://kllc.github.io/repo/script/ad.js"; data() { return { ad: ad(), }; 最後に markdown をプッシュすると、Github Pages でホストされるブログができる。 めちゃくちゃ便利。最高。 これから機能を追加していこう。検索機能は作りたい。簡単にできるはず。 他の機能も順次・・って思ったけど、よく考えたら、閲覧回数とか、コメント機能とか、いいねとか。 そういったブログの基本機能は静的サイトじゃ無理ですね。 じゃあブログじゃないやん。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【実装編】Vue.jsで再利用可能なコンポーネントを実装する方法

props編 slot編 実装編?今ここ はじめに どうすれば再利用可能なコンポーネントを実装することができるのか? 「props」と「slot」を使って実装してみます。 バージョン Vue.js 2.6.11 実装 アコーディオンを用意しました。 必要に応じてタイトル右横に「update」「new」バッジが付与できます。 App.vueでAccordion.vueをimport Accordionタグの中に名前付きslotを使用してコンテンツをコンポーネントに追加する propstype, required, default, validatorをそれぞれ指定 propsの種類としては typeA -> 何もなし typeB -> update typeC -> new App.vue <template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png" width="25%" /> <div> <p>アコーディオン</p> <Accordion variant="typeA"> <template v-slot:title>1.コンテンツについて</template> <template v-slot:content> Lorem ipsum dolor sit amet consectetur adipisicing elit. Sunt quaerat consequatur ipsum quia a quod sed cum quae maxime reprehenderit, porro sequi exercitationem voluptate saepe minus in ullam. Beatae, itaque! </template> </Accordion> <Accordion variant="typeB"> <template v-slot:title>2.不具合について</template> <template v-slot:content> Lorem ipsum dolor sit amet consectetur adipisicing elit. Sunt quaerat consequatur ipsum quia a quod sed cum quae maxime reprehenderit, porro sequi exercitationem voluptate saepe minus in ullam. Beatae, itaque! <br /> <br /> Lorem ipsum dolor sit amet consectetur adipisicing elit. Sunt quaerat consequatur ipsum quia a quod sed cum quae maxime reprehenderit, porro sequi exercitationem voluptate saepe minus in ullam. Beatae, itaque! </template> </Accordion> <Accordion variant="typeC"> <template v-slot:title>3.新コンテンツのお知らせ</template> <template v-slot:content> Lorem ipsum, dolor sit amet consectetur adipisicing elit. Facilis magnam ullam repudiandae, dolorem dignissimos debitis at possimus! Reprehenderit enim illum asperiores, nihil labore veniam vitae, aperiam id a rem consequatur. <a href="#">more</a> </template> </Accordion> <Accordion> <template v-slot:title> 4.その他</template> <template v-slot:content> 準備中... </template> </Accordion> </div> </div> </template> <script> import Accordion from "./components/Accordion"; export default { name: "App", components: { Accordion, }, }; </script> components/Accordion.vue <template> <div class="accordion" :class="[ { 'accordion--A': variant === 'typeA' }, { 'accordion--B': variant === 'typeB' }, { 'accordion--C': variant === 'typeC' }, ]" > <button class="accordion__button" @click="toggleAccordion()" :class="{ 'is-active': isOpen }" > <slot name="title" /> </button> <div class="accordion__content" v-show="isOpen"> <slot name="content" /> </div> </div> </template> <script> export default { name: "", props: { variant: { type: String, required: false, default: "typeA", validator: (value) => ["typeA", "typeB", "typeC"].includes(value), }, }, data() { return { isOpen: false, }; }, methods: { toggleAccordion() { this.isOpen = !this.isOpen; }, }, }; </script> 最後に 参考にどうぞ!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel8 + Vue3 + Typescript + Vuetify で Hello World しただけ

はじめに 今年のQiita記事の投稿数が5しかないという結果を受けてなんとか5を6にしてやろうと書いた記事です。 最近、Laravel8, Vue3, Typescript, Vuetify でアプリを作成したので備忘録として残します。 手順 1. Laravel アプリの作成 curl -s "https://laravel.build/laravel-vue3-typescript-vuetify" | bash docker-compose.ymlを整理 コンテナのビルドに時間かけるの嫌だったので、とりあえずlaravelのコンテナだけにしました。 docker-compose.yml # For more information: https://laravel.com/docs/sail version: "3" services: laravel.test: build: context: ./vendor/laravel/sail/runtimes/8.1 dockerfile: Dockerfile args: WWWGROUP: "${WWWGROUP}" image: sail-8.1/app extra_hosts: - "host.docker.internal:host-gateway" ports: - "${APP_PORT:-80}:80" environment: WWWUSER: "${WWWUSER}" LARAVEL_SAIL: 1 XDEBUG_MODE: "${SAIL_XDEBUG_MODE:-off}" XDEBUG_CONFIG: "${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}" volumes: - ".:/var/www/html" networks: - sail # depends_on: # - mysql # - redis # - meilisearch # - selenium # mysql: # image: 'mysql/mysql-server:8.0' # ports: # - '${FORWARD_DB_PORT:-3306}:3306' # environment: # MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' # MYSQL_ROOT_HOST: "%" # MYSQL_DATABASE: '${DB_DATABASE}' # MYSQL_USER: '${DB_USERNAME}' # MYSQL_PASSWORD: '${DB_PASSWORD}' # MYSQL_ALLOW_EMPTY_PASSWORD: 1 # volumes: # - 'sailmysql:/var/lib/mysql' # networks: # - sail # healthcheck: # test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] # retries: 3 # timeout: 5s # redis: # image: 'redis:alpine' # ports: # - '${FORWARD_REDIS_PORT:-6379}:6379' # volumes: # - 'sailredis:/data' # networks: # - sail # healthcheck: # test: ["CMD", "redis-cli", "ping"] # retries: 3 # timeout: 5s # meilisearch: # image: 'getmeili/meilisearch:latest' # platform: linux/x86_64 # ports: # - '${FORWARD_MEILISEARCH_PORT:-7700}:7700' # volumes: # - 'sailmeilisearch:/data.ms' # networks: # - sail # healthcheck: # test: ["CMD", "wget", "--no-verbose", "--spider", "http://localhost:7700/health"] # retries: 3 # timeout: 5s # mailhog: # image: 'mailhog/mailhog:latest' # ports: # - '${FORWARD_MAILHOG_PORT:-1025}:1025' # - '${FORWARD_MAILHOG_DASHBOARD_PORT:-8025}:8025' # networks: # - sail # selenium: # image: 'selenium/standalone-chrome' # volumes: # - '/dev/shm:/dev/shm' # networks: # - sail networks: sail: driver: bridge # volumes: # sailmysql: # driver: local # sailredis: # driver: local # sailmeilisearch: # driver: local Sailを使ってローカルで立ち上げ もし何かエラーでたら、こちらご覧ください。 ./vendor/bin/sail up Vue3系の設定 インストール ./vendor/bin/sail npm install vue@next vue-loader@next @vue/compiler-sfc webpack.mix.js の編集 webpack.mix.js const mix = require("laravel-mix"); /* |-------------------------------------------------------------------------- | Mix Asset Management |-------------------------------------------------------------------------- | | Mix provides a clean, fluent API for defining some Webpack build steps | for your Laravel applications. By default, we are compiling the CSS | file for the application as well as bundling up all the JS files. | */ mix.js("resources/js/app.js", "public/js") .postCss("resources/css/app.css", "public/css", [ // ]) .vue(); //追記 コンポーネントの作成 app.blade.php の作成 app.blade.php <!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"></div> </body> </html> resources/js/components/App.vue <template> <div>{{ message }}</div> </template> <script> import { defineComponent, ref } from "vue"; export default defineComponent({ setup() { const message = "Hello World"; return { message }; } }); </script> ルーティング追加 routes/web.php Route::get('/app', function () { return view('app'); }); ビルド ./vendor/bin/sail npm run dev とりあえず Vue3は導入できました。 Typescript インストール ./vendor/bin/sail npm install typescript ts-loader @types/webpack-env -D app.js を app.tsにファイル名変更 それと同時に、webpack.mix.jsも変更 webpack.mix.js const mix = require('laravel-mix'); mix.ts('resources/js/app.ts', 'public/js') //mix.jsをmix.tsに、app.jsをapp.tsに変更 .postCss('resources/css/app.css', 'public/css', [ // ]).vue(); mix.ts に変更できてなかったら、ビルドした際にこんなエラー出るのできちんと変更しましょう。 Module build failed (from ./node_modules/babel-loader/lib/index.js): SyntaxError: /var/www/html/resources/js/components/App.vue: Unexpected token, expected "," tsconfig.json作成 tsconfig.json { "compilerOptions": { "target": "esnext", "module": "esnext", "strict": true, "jsx": "preserve", "importHelpers": true, "moduleResolution": "node", "esModuleInterop": true, "allowSyntheticDefaultImports": true, "sourceMap": true, "baseUrl": ".", "typeRoots": ["./resources/js/@types"], "lib": ["esnext", "dom", "dom.iterable", "scripthost"] }, "include": [ "resources/js/**/*.ts", "resources/js/**/*.tsx", "resources/js/**/*.vue" ], "exclude": ["node_modules"] } @types以下の作成 resources/js/@types/shims-vue.d.ts declare module "*.vue" { import {defineComponent} from "vue"; const component: ReturnType<typeof defineComponent>; export default component; } App.vue 変更 App.vue <template> <div>{{ message }}</div> </template> <script lang='ts'> //変更 import { defineComponent, ref } from "vue"; //変更 export default defineComponent({ setup() { const message = ref<string>("Hello World"); //変更 return { message }; } }); </script> Vuetify の導入 sail npm install vuetify@next -D app.tsの変更 resources/js/app.ts import "vuetify/styles"; import { createApp } from "vue"; import { createVuetify } from "vuetify"; import * as components from "vuetify/lib/components"; import * as directives from "vuetify/lib/directives"; import App from "./components/App.vue"; const app = createApp(App); const vuetify = createVuetify({ components, directives }); app.use(vuetify); app.mount("#app"); App.vueの変更 resources/js/components/App.vue <template> <v-app> <v-card> <v-card-title>{{ message }}</v-card-title> </v-card> </v-app> </template> <script lang="ts"> import { defineComponent, ref } from "vue"; export default defineComponent({ setup() { const message = ref<string>("Hello World"); return { message, }; }, }); </script> tsconfig.json tsconfig.json { "compilerOptions": { "target": "esnext", "module": "esnext", "strict": true, "jsx": "preserve", "importHelpers": true, "moduleResolution": "node", "esModuleInterop": true, "allowSyntheticDefaultImports": true, "sourceMap": true, "baseUrl": "./", "typeRoots": ["resources/js/@types"], "types": ["vuetify"], ← これを追記 "lib": ["esnext", "dom", "dom.iterable", "scripthost"] }, "include": [ "resources/js/**/*.ts", "resources/js/**/*.tsx", "resources/js/**/*.vue" ], "exclude": ["node_modules"] } sail npm run dev これでいけるはず レポジトリも載せておきます。 おわりに 僕はこれにvue-router を加えて開発しました。 次は nuxt3 x Vuetify3 x Typescript でやってみたいですね。 間違ってるところとか、これ通りにできなければ教えてください。 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SpringBootでAPI提供する時のCORS接続エラーを解決する

経緯 Vue.js と SpringBoot を用いて SPA 開発をしていたところ、API が叩けない問題が発生しました。 Postman 等のツールで API を確認すると Spring は正常に機能しているのに、いざ Vue や React から API を叩こうとするとエラーが出てしまう状況でした。 そこでフロントとバックエンドの接続から、値の受け渡しができるまでを記述していこうと思います。 結論 結局の所、SpringBoot 側の問題でした。 具体的には、他のプログラムからは接続できないようにセキュリティがかかっているとのこと。 そこで今回は、指定した URL からは API を叩けるように指定していく。 Docker コンテナ間でも接続確認できました。 SpringBoot の記述 今回は、 Vue から { email: ○○, password: ☓☓ } を送信し、Spring で受信内容を出力 Spring から { result: OK } を返却する ディレクトリ構成 app ├src/main/java/com/example | ├ controller - SampleController.java | ├ form - LoginForm.java | └ common - CorsConfig.java └ build ... SampleController.java SampleController.java package com.example.controller; import java.util.HashMap; import java.util.Map; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.example.form.LoginForm; @RestController @RequestMapping("/user") public class SampleController { @PostMapping("/login") public Map<String, String> login(@RequestBody LoginForm form) { System.out.println(form); Map<String, String> sample = new HashMap<>(); sample.put("result", "OK"); return sample; } } CorsConfig.java CorsConfig.java package com.example.common; import java.util.Arrays; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Arrays.asList("http://localhost:8081")); // アクセス許可するURL config.setAllowCredentials(true); config.addAllowedMethod("*"); config.addAllowedHeader("*"); config.addExposedHeader("Set-Cookie"); UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); configSource.registerCorsConfiguration("/**", config); return new CorsFilter(configSource); } } 起動してみる Vue - SpringBoot 間で値の受け渡しができました。 最後に 今回はローカル環境上で CORS エラーを解決できましたが、Docker を使ったコンテナ間でのやり取りはまだ CORS エラーが出たままとなっているので、次は Docker 環境上でも機能する仕様にしていきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ajax -> Vueへ自作オンラインゲームの進化

以前、いくつか既存のボードゲーム(カード型)をオンラインで遊べるようにしてみた。 ユーザ作成、ゲーム作成は流用し、ロジックだけ(これがゲームの9割を占めるが)を修正することで10個以上のゲームをオンラインで遊べるようにしてみた。 そのときに使っていた技術としては FrontEnd: Html, Ajax, JQuery BackEnd: Server: Heroku Language: Python Framework: Flask Data: Redis と言う構成で作っていた。 思ったこと Ajax, JQueryの限界 まぁ、Ajaxは通信ライブラリなので、そこまで限界とは思っていないが、一番やばかったのはJQuery。 いや、これもすごい技術ではある。 今まで、ページをリフレッシュしないと更新されなかったオブジェクトを、リアルタイムに変更できるから。(一昔前はね) が、現代ではそれが当たり前の上で、いかの効率よく作るか、、、と言うことが考えられてきている。 その中で、JQueryは以前から見直されてきているのがJavaScript系のフレームワーク。 Reactや、Backbone.jsなどのフレームワークが出始め、最近ではVueが注目を浴びている なぜ最近のフレームワークが注目されるのか ひとえに、更新されたデータが画面のリフレッシュなく参照できる技術が進化し、よりリアルタイムに、開発効率が良くなるような技術方法に進化してきているかと思われる。 どう変わったのか HTMLの時代 このときは、1ページ毎に入力画面、完了画面と言うのを実装し、更新結果は次のページに遷移しないと見れない。と言うのが当たり前だった ┌──┐ ┌──┐ │入力│→│完了│ └──┘ └──┘ input.html <body> <form action='complete/' method='get'> <input type='text' name='id' /> <input type='submit' value='確認' /> </form> </body> complete.html <body> ID: test transaction id: 1234 </body> JavaScriptで制御するのは、入力内容のチェックだったり、表示/非表示ぐらい フロントエンドはシンプルそうに見えるが、応答するHTMLはサーバ側で生成するため、画面遷移が必要 こちらのHTMLや、JavaScriptはイメージとして捉えてください。このまま実装して動くものではありません JQuery/Ajaxの時代 ここから、上記のような1ページ毎に処理をしなくても、1ページ内で通信を行い、その結果を取得し、同じページを更新する。と言う現代のベースになる概念となった page.html <body> <!-- 入力画面 --> <div id='input'> <input type='text' name='id' id='id' /> <input type='button' name='submit_input' value='確認'/> </div> <!-- 完了画面 --> <div id='complete' style='visibility:hidden;'> ID: <div id='id'></div> trainsaction id: <div id='transaction_id'></div> </div> <script> $("#submit_input").on( "click", function( event ) { $.ajax({ url: "complete?id=" + $("#id").text(), data: { id: $("#id").val() }, success: function( result ) { $("#input").hide(); $("#complete").show(); $("#id").text($("#id").text()); $("#transaction_id").text(result); } }); }); </script> </body> こちらのHTMLや、JavaScriptはイメージとして捉えてください。このまま実装して動くものではありません そしてVueな世界 page.html <body> <!-- 入力画面 --> <template v-if="type == 'input'"> <input type='text' v-model='inp_id' /> <button @click='submit_input'>確認</button> </template> <!-- 完了画面 --> <template v-if="type == 'complete'"> ID: {{ inp_id }} transaction id: {{ transaction_id }} </template> <script> function submit_input() { asyncData: async function() { let result = await axios.get('complete?id=' + inp_id); return { transaction_id: result}; } } var app = new Vue({ el: '#app', }) </script> </body> こちらのHTMLや、JavaScriptはイメージとして捉えてください。このまま実装して動くものではありません ここまでのまとめ 最後の2つ、JQueryと、Vueでの違いと言うところでは、JavaScriptで制御している点は同じですが、HTMLとのデータのやり取りの際に、JQueryの場合はどのid/classと言う指定が目立つのですが、VueについてはすべてJavaScript内の変数名で記述が統一できていることがわかっていただけるかと思います。 私が有用と思う点をまとめてみました 私が思うVueの利点 データを更新する際、わざわざJavaScript側でオブジェクトを指定する必要がない(単にJSとして変数の値を更新するだけ) 上記については反映する項目数が多くなった場合にかなり有利になってくる Vueとは直接関係ないが、サーバとの通信の際、JSON形式であればaxiosを使うと、そのままJSの配列として扱える(加工処理がいらないし、いきなり非同期) データの更新タイミングをsetTimeoutなどで制御する必要がない(JQueryの場合は、JSのデータが変更になっても、更新するためのトリガーを用意する必要がありますが、Vueではそれがリアルタイムなので、JSのデータが変更されれば、勝手に更新される)※これが一番大きい ということで Vue化したバージョンは、今後、リリース予定です。 乞うご期待ください。 それによりシンプルではありながら、JQueryでは制御が難しかったところを解消し、より使いやすいアプリにできればと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む