20210418のvue.jsに関する記事は10件です。

【Vue】ログイン→ローディング画面→ルーティング【詰まったこと】

概要 vueとfirebaseを用いてログインを実装。 ログイン ルーティング ログインが確認できたら特定のページにルーティング。 その際、ユーザーをセットするまでに時間がかかりルーティングに待ちが生じる。 この待ち時間にローディング画面を入れたい。 ライブラリ firebase vuex vue-router vue-loading ※それぞれ細かい解説は省く。 ログイン ログインはGoogleの認証を使用。 認証画面に自動で飛ぶため、ログインユーザーを選択。 ※vuexより抜粋 // vuex/actions login() { const provider = new firebase.auth.GoogleAuthProvider(); // 自動でグーグルの認証画面 firebase.auth().signInWithRedirect(provider); }, firebaseのonAuthStateChangedメソッドで現在ログインしているユーザーを取得。 ログインユーザーをstate(vuex)にセット。 // App.vue created() { // ログインログアウトを検知 // ログイン時とログアウト時にユーザーオブジェクトが入る firebase.auth().onAuthStateChanged(user => { if (user) {      //ユーザーをセット this.setLoginUser(user); // ログアウト時 } else {      //セットしたユーザーを削除 this.deleteLoginUser(); } }); }, 一応セット用のコード // vuex/actions setLoginUser({ commit }, user) { //mutationsでstateのlogin_userでuserをセット commit("setLoginUser", user); }, ルーティング ユーザーがログインしたのち特定のページへルーティング。 先ほどのコードに少し加えるだけ。 // App.vue created() { // ログインログアウトを検知 // ログイン時とログアウト時にユーザーオブジェクトが入る firebase.auth().onAuthStateChanged(user => { if (user) { this.setLoginUser(user); // ルーティング if (this.$router.currentRoute.name === "home") { this.$router.push({ name: "input" }); } // ログアウトした時 } else { this.deleteLoginUser(); } }); }, この処理を実行した時inputへページ遷移はできるもののユーザーのセットに時間がかかりルーティングまでに待ちが生じる。 ローディング画面 vue-loadingを使用。 導入方法などは【Vue.js】ローディング画面の実装方法(サンプルコード付き)を参照。 // vuex/state const state = { loading: false }; loadingの値によってローディング画面をトグル。 (App.vue) <template> <div id="app"> <Loading v-show="loading"></Loading> <div class="container" v-show="!loading"> <Header></Header> <div class="content"> <router-view></router-view> </div> </div> </div> </template> <script> import { mapState } from "vuex"; export default { computed(){ ...mapState("loading", ["loading"]) } </script> あとはactionsでloadingの値をトグルするだけだが、どこで実装すればいいかわからなかった。 色々試した結果行き着いたのがナビゲーションガードによるルーティング処理。 試したこと loadingの初期値はfalseなため、loadingを発動させるにはtrueにする必要がある。 したがってルーティングする前にloadingの値を変えれば良い。 userオブジェクトの確認後セットされるタイミングと考えた。 ここで詰まったため動かなかったコードを先に紹介。 // App.vue created() { // ログインログアウトを検知 // ログイン時とログアウト時にユーザーオブジェクトが入る firebase.auth().onAuthStateChanged(user => { if (user) {      //ユーザーをセット this.setLoginUser(user); // actionsによりloadingの値を変更 this.setLoading(true); // ルーティング if (this.$router.currentRoute.name === "home") { this.$router.push({ name: "input" }); } // ログアウト時 } else {      //セットしたユーザーを削除 this.deleteLoginUser(); } }); }, 次にloadingの値をfalseにしてloading画面を終わらせる必要がある。 こちらも試行錯誤したが、最終的になんてことなかったので正解のコードを紹介。 // input.vue // loading画面を無効 beforeRouteEnter(to, from, next) { next(vm => { vm.setLoading(false); }); }, こちらの2セットで動くと考えたがどうやらローディング画面が一瞬で終わってしまう。 動きとしては, 1. ログインユーザーを選択 2. homeコンポーネントの画面で固まる 3. 一瞬ローディングが入ってinputコンポーネントの画面に移る 結果 なぜこのような動きになってしまうのか、あくまで推測だがuserオブジェクトの処理に時間がかかっていたからである。 この処理が実行された後loadingの値を変更していたため、本来潰したかった待ちの時間が埋まらない結果に。 したがってuserオブジェクトを処理する前にloadingの値を変更する必要がある。 次に試してみてとりあえず動いたコード // App.vue created() { // actionsによりloadingの値を変更 this.setLoading(true); // ログインログアウトを検知 // ログイン時とログアウト時にユーザーオブジェクトが入る firebase.auth().onAuthStateChanged(user => { if (user) {      //ユーザーをセット this.setLoginUser(user); // ルーティング if (this.$router.currentRoute.name === "home") { this.$router.push({ name: "input" }); } // ログアウト時 } else {      //セットしたユーザーを削除 this.deleteLoginUser(); } }); }, userが入ってくる前にトリガーしているため、確かに思い通りに動く。 しかし一つだけ大きな問題がある。 初期描画 ログインユーザーが存在しない状態でアプリを開いたとする。 するとログインできないままloadingがtrueになってしまい、無限ローディングが始まってしまう。 解決策 なんとかゴリ押しで解決できた。 // App.vue created() { // actionsによりloadingの値を変更 this.setLoading(true); setTimeout(() => { this.setLoading(false); }, 2100); // ログインログアウトを検知 // ログイン時とログアウト時にユーザーオブジェクトが入る firebase.auth().onAuthStateChanged(user => { if (user) {      //ユーザーをセット this.setLoginUser(user); // ルーティング if (this.$router.currentRoute.name === "home") { this.$router.push({ name: "input" }); } // ログアウト時 } else {      //セットしたユーザーを削除 this.deleteLoginUser(); } }); }, 初期描画の段階でローディングが入るものの、2.1秒後にloadingをfalseにする処理を入れることによってローディングを止めることができた。 あまりにパワープレイでダサいため気に入ってないが、私にはこれが限界である。 他にいい案があれば教えていただきたい。 気付き nextの処理 // input.vue // loading画面を無効 beforeRouteEnter(to, from, next) { next(vm => { vm.setLoading(false); }); }, この beforeRouteEnter ガードは this へのアクセスはできないです。なぜならば、ナビゲーションが確立する前にガードが呼び出されるからです。したがって、新しく入ってくるコンポーネントはまだ作られていないです。 しかしながら、 next にコールバックを渡すことでインスタンスにアクセスすることができます。このコールバックはナビゲーションが確立した時に呼ばれ、コンポーネントインスタンスはそのコールバックの引数として渡されます。 vue-router公式より 書いてるままだが、beforeRouteEnterを使った場合でもvmを使うことによってインスタンスにアクセスできる。 以上。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsをコーディングする上で個人的に気をつけていること

はじめに 1人〜3人程度の小規模のプロジェクトでVue.jsを書き始めて約半年ほど経ち、設計のナレッジが少し溜まりましたので、そのナレッジのアウトプットとしてVue.jsをコーディングする上で個人的に気をつけていることを羅列していきたいと思います。 Vue.jsのバージョンは2.x系を想定しています。 またstorybook, SSRなどのフロントエンド独自の領域はほとんど扱わず、設計に関わる話を中心に記載していきます、なお投稿の中でMVVMやFluxの言葉をよく使います。 SPAにはSPAにあった設計を まず従来のWeb開発ではバックエンド側で画面にて使用する変数を描画前に用意してから、HTMLに変数を埋め込むという手法でViewModelを実現していました。 例えばLaravelフレームワーク 8.xの場合だと、下記のようになると思います。 HomePresenter.php <?php namespace App\Http\Controllers; use Illuminate\Support\Auth; class HomePresenter extends Controller { /** * ホーム画面を表示する. */ public function showLoginPage() { $loginUser = Auth::user(); $group = $loginUser->getGroup(); // 画面に必要なデータをViewModelとしてバックエンドで拵える $groupName = $group->name; $loginUserName = $loginUser->name; $members = $loginUser ->getGroup() ->getMembers() ->select(['name', 'icon_path'])->map(function ($member) { return [ 'name' => $member['name'], 'iconPath' => $member['icon_path'], ]; }); return view('home', compact('groupName', 'loginUserName', 'members')); } } <?php /* home.blade.php */ ?> <header> <nav> <div> {{ $groupName }} </div> <div> {{ $loginUserName }} </div> </nav> </header> <main> <ul> @foreach ($members as $member) <li> <div> <div> <img src="{{ $member->iconPath }}" alt="{{ $member->name }}"> </div> <div> {{ $member->name }} </div> </div> </li> @endforeach </ul> </main> しかしAjax, SPAの登場により、画面推移を伴わない画面の更新が可能になりました。 SPAの場合は1つの画面で様々なインタラクティブな操作を提供するため、View, ViewModelが複雑になり、Modelが複数箇所から参照されるようになってきます。 なのでSPAを構築する上では複雑化する画面の動作の可読性や保守性など、SPAならではのいくつかの考慮すべきポイントが出てくると思っています。 以下ではその考慮ポイントをダラダラと列挙していきたいと思います。 DOMを直接扱わない React、Vue.jsなどのSPA向けフレームワークでは仮想DOMという技術を採用しています。 仮想DOMについての詳細な説明は扱いませんが、端的に表現するとDOMの直接更新に仮想DOMは追従しません。 この事象に起因するバグは往々にして原因が追跡しづらく動作が不安定です。 JavaScript、jQueryに慣れているとついついDOMを直接操作をしてしまいますが、SPA向けのフレームワークではフレームワーク側で用意されたViewModelのみを参照すると良いと思います。 Vueの公式スタイルガイドの記載内容を取り入れる 優先度 Aな規約に関してはほとんどプロジェクトに適応可能だと思いますので、取り入れておくと良いと思います、優先度 B以降の規約に関してはプロジェクト毎で考える範囲かなと思います。 Vue 2.x スタイルガイド コンポーネントを4種類に分類する コンポーネントの分類方法はかなりプロジェクト毎に変わる部分ではありますが、最低限コンポーネントを4つに分類することで、テスト容易性、保守性、可読性、拡張性などを向上させることができると思います。 その分類とは単一ファイルコンポーネント、密結合コンポーネント、基底コンポーネント、単一インスタンスです、Vueのスタイルガイドにも同様の記載があります。 大まかな使い分けとしては下記のようになると思います。 ページそのものは単一ファイルコンポーネントとして定義する そのページでしか使われない様な用途の限られたコンポーネントは密結合コンポーネントとして定義する どのページでも共通なヘッダー、フッターは単一インスタンスとして定義する 入力欄やボタンなど、使いまわしをするものは基底コンポーネントとして定義する 僕自身は上記の分類を守るために下記の様なディレクトリの切り方が多いです、密結合コンポーネントを配置するために画面毎にディレクトリを切っています。 project/ ├── components │ ├── base # 基底コンポーネントの配置場所 │ │ └── Input.vue │ └── singleton # 単一インスタンスの配置場所 │ ├── Header.vue │ └── __tests__ │ └── Header.test.vue └── pages # 単一ファイルコンポーネントの配置場所 └── Login # 密結合コンポーネントの配置場所 ├── Login.vue ├── LoginForm.vue └── __tests__ ├── Login.test.vue └── LoginForm.test.vue バックエンドとのやり取り ページ表示前にViewModelをバックエンドで用意し、ページの初期表示時にViewModelを渡そうとすると、親コンポーネントにPropが大量に宣言され可読性が下がる、Propのバケツリレーが行われるなどの問題が発生します。 Parent.vue <template> <!-- 省略、またここでpropsのバケツリレーが行われる --> </template> <script> export default { props: { groupName: { required: true, type: String }, loginUserName: { required: true, type: String } // 以降配下のコンポーネントに必要なPropが宣言されていく...。 // コンポーネントがPropに強く依存していて、可読性と保守性が下がる }, } </script> 上記を回避するためにフロントエンドとバックエンドのデータの取得/更新は極力非同期通信にて行うようにすると、親コンポーネントに必要なデータの宣言のみを行うことができ、コンポーネントの責務を明確に分離できます。 また後述するテスト容易性の観点からAPIとの通信などの非同期処理はオブジェクトとして独立させるようにすると良いです。 Parent.vue <template> <!-- 省略 --> </template> <script> export default { inject: ["$api"], data: { // このコンポーネントに必要なデータのみ宣言すればいい loginUserName: () => "" }, mounted: function () { const user = this.$api.getLoginUser(); this.loginUserName = user.name; } } </script> ただ非同期通信の処理を増やすことでデータのやり取りのオーバーヘッドや、都度都度発生する例外処理や、API通信の待ち時間によるユーザーのストレスなどのデメリットもあります。 そういった場合はローディング処理を入れることでユーザーのストレスを解消し、オーバーヘッドを許容する設計や、例外処理をテストコードによってテストするなどの規約などを設けて、ネットワークの問題による非同期通信の失敗パターンなどを事前検証するなどの対処をすると良いと思います。 コンポーネント間を跨いだデータのやり取り SPAを用いた場合、コンポーネントを跨いてデータを扱う場合が多少あります、そういった場合には、様々な手段が取れますが状況に応じて親コンポーネントのPropを介してやり取りするかVuexを利用するかのどちらかを選択すると良いと考えています。 例えばボタンをクリックした場合、他のUIコンポーネントの表示が1箇所だけ切り替わるといった場合は、データの依存関係が複雑ではないので、Props down/event upな設計で良いと思います。 しかしボタンをクリックすると他の複数のUIコンポーネントの表示や振る舞いが変わる場合には、Vuexでデータを持つと良いと思います、例えばダークモードなどの実装などはデータはVuex側に持たせるべきだと思います。 またAPI通信をActionsに含める、Modelの振る舞いをGettersに定義するなどModelに関する知識をVuex側で定義すると、Modelに知識を集約することができます。 Modelに知識を集約することで簡潔なテストを記述できる、Modelの更新ルートを局所化できる、Modelの振る舞いを共通化できるなどの様々なメリットがあります。 Vuexを採用する際には、vuex-smart-moduleなどの適切な型付けとActions、Mutationsのモック化ができるライブラリを利用すると保守性を向上させることができます。 テストコード まず本項で扱うテストコードとは関数に対する単体テストや、Vue ComponentやVuexに対する結合テストを指しています。 僕が思うに今後保守される可能性があるコードを書くうえで、テストコードが残っているとフロントエンドの保守性が上がると考えています。 テストコードのメリットは世にたくさん出ていますが、基本的には下記の4点に絞られると思います。 1. コンポーネントの振る舞いに関するドキュメント テストコードは、実装コードの振る舞いを語る動くドキュメントとして機能します。 2. コンポーネントの独立性に寄与 依存性が少ないコードはテストがしやすく、再利用性が高くなります。 テストコードを書くことで、実装の独立性は自ずと高くなると思っています。 3. コンポーネントのバグを減らすことに寄与 テストコードを記述することによって、コードが意図した振る舞いになるかテストすることができます。 テストツールと合わせてカバレッジ計測ツールを利用することで、実装コードのテスト網羅性を計測できます。 テストの抜け漏れを防ぎ、思わぬバグを防ぐことができます。(例えばawaitのつけ忘れとか、複雑なif分岐とか) 4. 再実行可能なテスト テストコードはプログラムによってプログラムをテストするので、人手とは異なり正確に迅速にテストを実行できます。 またテストコードを書く上で再現性がないテストコードを書いてしまう、観点がバラけたテストが散見するなど回避するべき事象がいくつかあります。 そういった事象を回避するために良いテストのポイントを以下に記載します、テストツールはJestを想定しています。 テストの可読性を考慮する 例えばVuexでuser modelからrole typeが管理者であるユーザーのみを取得する処理を考えてみます。 users.ts export const Role = { staff: 0, admin: 1 } as const; export type RoleType = typeof Role[keyof typeof Role]; export interface User { id: number; name: string; role: RoleType; } export interface UserListState { users: User[] } export const users = { state: { users: [] }, getters: { getAdminUserList(state: UserListState) { return state.filter(user => user.type === Role.admin).map(user => { return { id: user.id, name: user.name }; }); } } }; 上記のテストを作る上で、実装コードと同様のコードをテストに書いてしまう、3Aを守らないなどテストコードの可読性を下げてしまうようなパターンがあります。 users.test.ts import { users } from "@/store/modules/users"; describe("users", () => { // 何をテストするのか不明瞭でわからない it("getAdminUserList", () => { const userListState = [ // パット見なんのユーザーがわからない { id: 99, name: "user-1" role: 0 }, { id: 100, name: "user-2" role: 1 } ]; // テストコードと同じ実装コードを書いてしまっている const ret = userListState.filter(user => user.type === Role.admin).map(user => { return { id: user.id, name: user.name }; }); // 3Aが守られていなく、長くて読みづらい expect(user.getters.getAdminUserList(users)).toBe(ret); }); }); テストコードはドキュメントとして機能するはずなので、可読性を上げて保守したいものです、以下に改善したものを記載します。 users.test.ts import { users, Role } from "@/store/modules/users"; // users moduleの責務が記載されている describe("ユーザーのリストを扱うusers module", () => { // テスト対象の振る舞いが記載されている it("ユーザーリストの中から権限種別が管理者に一致するユーザーを取得できる", () => { const userListState = [ // ユーザーの権限が適切に宣言されている { id: 99, name: "staff" role: Role.staff }, { id: 100, name: "admin" role: Role.admin } ]; const actual = users.getters.getAdminUserList(userListState); // 関数な返り値が処理ではなく、値として宣言されている const expected = [ { id: 100, name: "admin" role: Role.admin } ]; // 3Aが守られていて、Assertの可読性が高い expect(actual).toBe(expected); }); }); 上記のようにテストコードの可読性を上げるために、テストコードに処理は書かず、処理の入出力に必要な値をリテラルな値で宣言する、3Aを意識してテストコードの流れを意識する、テストケースの切り方を規約化するなどのルールを設けてテストコードの可読性を向上させていくと良いと思います。 テストの再実行性を考慮する テストコードは改修時の影響範囲の確認のために実行されるかもしれませんし、ソースコードがコミットされる度にCI/CDパイプライン上で実行される可能性もあります。 良いテストコードは再実行性を兼ね備えているはずなので、変数の宣言場所を留意する、時間に関わる処理はなるだけ時間を固定化できるように設計するなど、いつ再実行しても成功するように再実行性を考慮するようにすると良いと思います。 テストコードでカバー出来る品質は担保する 品質に関しては抑えて置きたいポイントがいくつかあります。 テストコードでは関数やVue Componentの振る舞いをテストできます、テストツールのカバレッジ機能を使ってテスト対象の振る舞いの抜けがなるだけなくなるようにしていくと、思わぬバグの混入を防ぐ可能性が高まります。 正規表現や境界値テストがマッチする様な処理はカバレッジのみを指標とせず、目視によって条件が網羅するようにすると思わぬバグを防ぐことができるかもしれません。 mock/stubにロジックを書かないほうがいいです、バグの混入に繋がってしまいます。 ただここで注意したいのが下記の様な目視のレビューで済むようなグルーコードもテストして時間を使ってしまうことです。 const stream = await this.$window.navigator.getUserMedia({ audio: true, video: { width: 1280, height: 720 }); getUserMediaメソッドの呼び出しをテストすることはもちろん可能ですが、それはメリットが労力に見合っていません、というかほとんどメリットがありません。 ブラウザやライブラリのAPIを呼びだす、bootstrapの様な共通処理をインポートするなどロジックではなくいわゆるグルーコードと呼ばれる部分の検査をレビューで実施するなど、実装コードのどの部分をテストするか?の観点は必要になります。 カバレッジを常に100%達成することが重要なのではなく、コードのロジックの部分や振る舞いなどが網羅的に検証されていることが重要だと思っています。 テストコードでカバー出来ない範囲を考慮する 例えばVue ComponentがDOMにマウントされ、ブラウザからどのように動作するか?という部分は基本的にブラウザ上でしか確認できません、テストコードが動作するのはあくまでNode.js上での話です。 なのでテストコードを書いた上で最終的な動作確認として、ブラウザからの確認は必要になってくると思います。 テスト容易性を考慮する 例えばバックエンドのAPIを呼び出す、ブラウザのAPIを呼び出す(例えばLocationやMedia Stream API)などテストをしているとフロントエンドの範疇を超えてくる存在が登場してきます。 そういった場合はMockオブジェクトに差し替えせるように設計しておき、テスト容易性を確保しておくことなどの考慮が必要になります。 具体的な対処としてはwindowオブジェクトは実装コードからはグローバル変数として参照せず、依存オブジェクトとして参照する、APIアクセスはオブジェクトとして定義するなどです。 テストデータはテストケース毎に作成する テストの再実行性で少し触れましたが、テストコードは将来的に再実行される可能性が高いので、テストコードは常に成功するようしておきたいです。 よって新規のテストケースの追加やあるテストケースの修正によって他のテストが失敗するなどの事象は避けたい所です。 テストをしていると関数の引数に渡すデータやモックオブジェクトを扱うことになりますが、これらのテストデータは共通化せず個々のテストごとに作成すると良いです。 というのも例えば共通なテストデータを参照している3つの関数があったとして、1つの関数のインターフェースの変更に伴いテストデータを更新すると、残りの2つの関数のテストが失敗する可能性があるからです。 共通化すべきものとそうでないもの UIが似ている、コンポーネントの振る舞いが似ていると理由でコンポーネントや処理を共通化するなどはなるだけしないほうがいいです。 例えばHTTPリクエストを発行する際に、認証が必要な場合にヘッダにTokenを付与する、サーバーから取得したCSRFトークンをVuexに保存する等のどの様な場合にも共通であって、将来に渡って処理が変更される可能性が低いものは共通化するべきだと思いますが、UIの場合はデザインや機能が変更される可能性が高くなりますので、共通化に関しては慎重に進めると良いと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】methodsでバインドした値をsubmitする際に上手くpostされない時にnextTickを使った話

Qiitaの質問機能で回答頂いた内容がとても勉強になったので、 復習を兼ねて記事としても残しておく。 発生した問題 vueテンプレート内で、form要素のaction属性、input要素のvalue属性を動的に処理したいが、 methods内でdataに対して、属性値を更新しても上手く代入した値が反映されず、空でpostされてしまう。 事例 vue.js 略 <form :action="action" method="post" ref="form"> <input :value="id" type="hidden" name="id"> </form> <span @click.prevent="formSubmit(item.id, '/edit')">編集する</span> 略 export default { data(){ return { action: '', id: '' } }, methods:{ formSubmit(id, action){ this.id = id; this.action = action; this.$refs.form.submit(); return false; } 略 ちなみに↓だと上手くいく vue.js 略 <form action="" method="post" ref="form"> <input value="" type="hidden" name="id" ref="input"> </form> <span @click.prevent="formSubmit(item.id, '/edit')">編集する</span> 略 export default { methods:{ formSubmit(id, action){ this.$refs.form.value = id; this.$refs.form.action = action; this.$refs.form.submit(); return false; } 略 解決方法 そもそもの問題はbindした結果がDOMに反映されるのが、 メソッドがreturnした後になる為、仮想DOMだけ書き換えて、 DOMそのものが書き換わる前に submitを実行する形となり、値がPOST出来なかった。 そこでnextTickを使ってsubmitをDOMの更新サイクル後に実行する。 vue.js 略 <form :action="action" method="post" ref="form"> <input :value="id" type="hidden" name="id"> </form> <span @click.prevent="formSubmit(item.id, '/edit')">編集する</span> 略 export default { data(){ return { action: '', id: '' } }, methods:{ formSubmit(id, action){ this.id = id; this.action = action; this.$nextTick(() => this.$refs.form.submit()); return false; } 略 nextTickはDOM更新後に第1引数で渡した関数を実行してくれる。 これでmethodsで更新した値でPOSTする事が出来た。めでたしめでたし。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuetify で特定の id リンクにジャンプする(Stringの場合、v-btnの場合)

経緯 Vuetifyをつかって、Rails で作成した id リンクに直接飛びたい。 しかし、一筋縄にはいきませんでした。 ボタンと文字、それぞれのリンクの付け方が異なっていたからです。 備忘録として残します。 String に idリンク をつけたい場合 <nuxt-link :to="{ path: `/users/${item.user_id}` }"> <span> Something </span> </nuxt-link> v-btn に idリンク をつけたい場合 <v-btn nuxt :to="`/users/${item.user_id}`" > Something </v-btn> 参考記事 Nuxt × Vuetifyの状況下でv-btnをnuxt-linkにして使いたい モーダルへのNuxtオープンリンク https://knews.vip/q/so73690053/mo-daru-e-no-nuxt-o-pun-rinku 以上です。{} や / や " など、どの記号が必要なのか総当たりで試しました。 この記事で、どなたかの時間が節約されるのであれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】パチンコ・パチスロの写真共有サービスを開発しました?

初めに pMemoriesというWebサービスを開発しました。 唐突にパチンコ・パチスロの画像を自慢したくなった&他人の自慢の一枚を見たくなったので作ってみました。 pMemoriesで何できるの?? pMemoriesはパチンコ・パチスロの思い出を共有できるWebサービスです。 みなさんのスマホの中にある自慢の自慢の一枚を投稿してください! ※いたずら投稿は悲しくなってくるのでやめてください>< 使用した技術 フロントとバックエンドをざっくり紹介します。 Nuxt.js フロントはNuxt.jsを使用しています。 ルーティング等が楽でとてもいいフレームワークですね・・・大好きです。 Laravel バックエンドはLaravelを使用しています。 こちらは業務等で使用して慣れているので使用しましたが、その内違う言語・フレームワークに変更することも考えています。 今後について 今回超短期間で開発したのでまだ「いいね」や「コメント」等のコミュニケーション機能は未実装、デザインも仮です。 今から開発していきますので期待して待ってくださると幸いです。(とりあえず出したかったんです!)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ラズパイでGitHub Actions runnerを動かす

はじめに ラズパイ + Vue.js + Electron で天気やゴミの日情報をTVに表示するアプリケーションを作った。 普段であれば、CircleCIを使用するところだが、ローカルネットワーク上のラズパイであること、折角なので普段使わないツールを使用してみたく、GitHub Actions Self-hosted runner を Raspberry Pi 3 + Raspberry Pi OS (旧raspbian) に構築し、自己デプロイを行えるようにしてみた。 Self-hosted runner とは Actionsのワークフローを、GitHub上にホスティングされたノードではなく、オンプレミス環境で実行するための runner ツール GitHub リポジトリの設定 runner を追加 Setting -> Actions から Self-hosted runners -> Add runner を選択 Download と token の発行 Operating System を Linux 、Architecture: を ARM に選択 ※Raspberry Pi OS (32bit)の場合 ※若干の茶番感は否めないのでこの手順自体はスキップしても問題ない Raspberry Pi 3 へ runner をインストール ダウンロード Actions / Add self-hosted runner の画面でOS等を選択すると、環境に合わせた Download を表示してくれるので、上から実行する # Create a folder mkdir actions-runner && cd actions-runner # Download the latest runner package curl -O -L https://github.com/actions/runner/releases/download/v2.278.0/actions-runner-linux-arm-2.278.0.tar.gz # Extract the installer tar xzf ./actions-runner-linux-arm-2.278.0.tar.gz 最新版のリリースを利用する場合は actions/runner の releases を参照 リポジトリと紐付け Actions / Add self-hosted runner 画面の Configure でトークンが発行されていることを確認できる 基本的には一つ前の手順から続けて実行すると、セットアップは完了する ./config.sh --url https://github.com/username/repository --token XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 対話式で質問がされるが、変更の必要がない限りは Return キー連打で問題ない runner の実行 ./run.sh 実行まで完了すると、Setting -> Actions 画面の Self-hosted runners に rasberrypi (ラズパイのホスト名) が追加され、状態が Idle となる ワークフローの設定 Actions の画面から New workflow もしくは Set up this workflow を押下するとエディタが起動する 実行を Self-hosted ノードとする場合、 jobs.build.runs-on の値を "self-hosted" とする 差分 - runs-on: ubuntu-latest + # runs-on: ubuntu-latest + runs-on: self-hosted buildの例 # This is a basic workflow to help you get started with Actions name: CI # Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the main branch push: branches: [ main ] pull_request: branches: [ main ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on # runs-on: ubuntu-latest runs-on: self-hosted # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 # Runs a set of commands using the runners shell - name: npm install run: | cd "${GITHUB_WORKSPACE}" npm i - name: Build run: | cd "${GITHUB_WORKSPACE}" npm run electron:build 最後に ラズパイだからと言って特別しなければならないようなことは無く、非常に簡単に構築ができた。 npm を使用したbuildの例も載せたので、参考にしてもらえると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DjangoでVue.jsを使う方法

はじめに DjangoのテンプレートにVue.jsを組み込み体時がある。 ただ普通に組み込もうとするとDjangoとVue.jsとでコンフリクトが起きてしまうので注意(以下で解説) コンフリクトが起きてしまう原因 DjangoとVue.jsでHTMLに組み込む際に{{}}が混同してしまい、 vue.jsの値が反映されなくなるのが原因 解決策 <div id="vue-app"> <h1>[[ myTitle ]]</h1> </div> <script type="text/javascript"> let app = new Vue({ el: "#vue-app", delimiters: ['[[', ']]'],  //これが重要 data: { myTitle: 'Hello Vue!', }, }); </script> 現在、DjangoとVue.jsでのアプリを制作中なのでつまづきやすい部分があればまた投稿します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsの復習

はじめに 1年ぐらい前に学習したVue.jsを実務で使う機会をいただきました! ということで、復習とアウトプットを兼ねて、ここに残すことにします。 この記事の対象者 Vue.jsを学習したけど忘れてしまった人 そんなのあったなぁ程度に思い返したい人 ※Vue.js初学者の方はこの記事を見てもあまり参考にならないので、実際にコードが記載されている記事を参考に手を動かすことをお勧めします Vue.jsの基礎要素 プロパティ dataプロパティ vueインスタンス内で保持する変数 呼び出す時は{{ 変数名 }}で可能 ■サンプルコード sample(呼び出し) <p>{{ message }}</p> sample(定義) data(){ return { message: 'こんにちは' //?messageという変数名で、値がこんにちは }; } methodsプロパティ インスタンス内で定義された関数 呼び出す時は{{ メソッド名() }}で可能 メソッド内で利用していないdataが更新された場合でも勝手に実行される ■サンプルコード sample(呼び出し) <p>{{ hello() }}</p> sample(定義) methods: { hello(){ return "hello"; } } computedプロパティ dataプロパティの変数を加工する時に使う methodsプロパティと混同しやすい 呼び出す時は{{ メソッド名 }} メソッド内で利用しているdataが更新された場合のみ実行される ■サンプルコード sample(呼び出し) <input v-model="name"> // ?v-modelに関しては「ディレクティブ」の節に記載しています <p>{{ nameToUpper }}</p> sample(定義) data(){ return { name: '' }; }, methods: { nameToUpper(){ return this.name.toUpperCase(); } } watchプロパティ dataの監視ができる 監視対象のdataが更新された時のみ実行される oldValueとnewValueがデフォルトで引数として渡される 非同期処理で利用されることが多い ■サンプルコード sample(呼び出し) <input v-model="memo"> // ?v-modelに関しては「ディレクティブ」の節に記載しています sample(定義) data(){ return { memo: '' }; }, watch: { memo(newValue, oldValue){ console.log('oldValue:',oldValue); console.log('newValue:',newValue); } } ディレクティブ vue.js特有の指示文のこと。 v-bindディレクティブ HTMLのタグの属性にインスタンスのプロパティ値をセットすることができる。 v-bind:属性名="プロパティ名"でセットする :属性名="プロパティ名"で省略可能 ■サンプルコード sample(呼び出し) <a v-bind:href="url">"url"で渡されたリンクに遷移します</a> // 省略パターン <a :href="url">"url"で渡されたリンクに遷移します</a> sample(定義) url: 'https://example.com' v-onディレクティブ イベントにインスタンスのメソッドを紐付けすることができる v-on:イベント名="メソッド名"で紐付けすることができる @イベント名="メソッド名"で省略可能 サンプルコード sample(呼び出し) <button v-on:click="myAlert">アラート</button> // 省略パターン <button @click="myAlert">アラート</button> sample(定義) mothods: { myAlert(event){ console.log('自作アラート', event); } } v-modelディレクティブ inputに入力された内容をインスタンスのプロパティに保持できる 保持した内容は即時反映される フォーム入力の更新時イベントで役立つ v-model="プロパティ名"で使用する サンプルコード sample(呼び出し) <input v-model="name" /> <p>{{name}}</p> sample(定義) data(){ return { name: '' } } このサンプルでは、inputに入力した内容が即時pタグのテキストに反映されます v-if、v-else、v-else-ifディレクティブ HTMLの中に条件を設定できる プログラミング言語のif文、else文、if-else文と同等 サンプルコード sample(呼び出し) <p v-if="number === 0">0です</p> <p v-else-if="number % 3 === 0">3の倍数です</p> <p v-else>分かりません</p> sample(定義) data(){ return { number: 3, ?この値によって表示される文言が変ります }; } v-forディレクティブ HTMLの中で繰り返しを行うことができる プログラミング言語のfor文と同等 サンプルコード sample(呼び出し) <p v-for="(favoriteWord, index) in favoriteWords" :key="index"> {{ favoriteWord.user }} : {{favoriteWord.text}} </p> sample(定義) data(){ return { favoriteWords: [ { user: '太郎', text: '成せばなる' }, { user: '二郎', text: '人は人、自分は自分' }, { user: '恵', text: '時は金なり' }, ], }; } 終わりに 今回の内容は本当に基礎的なことですし、私個人のメモレベルの内容です。 もっと学習して深く理解し、効率の良いフロント開発を行っていきたいと考えています。 次はコンポーネントについても記事を作ろうかな。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初学者がVue.jsをRailsアプリに組み込めるようになるまでの学習ロードマップ

この記事で得られること はじめに、この記事の目的について。 この記事は一通りRailsアプリケーションを作り終えて、次のステップとして「自分のアプリをモダンなUI/UXを実現したい!」「今っぽいイケてる技術を使ってみたい!」といった動機から、Vue.jsをRailsアプリケーションに組み込みたいと考えている人向けに、学習ロードマップを提供できたら、という狙いです。 以下学習で作成した成果物 私はこれから説明する学習を通して、初学者なりのポートフォリオを作成しました。 ベースはRuby on Rails、ランディングページにVue.jsを使ったアプリです。 StudyReport 最初にたどり着くページはVueのエントリーポイントを作成し、コンポーネントを並べるだけのシンプルな構成です。 またページ内の「Details」から飛べる機能の詳細説明ページは非常に簡単な部分SPAになっています。 こんなレベルでも、Vue.jsを使えるようになりたい!と考えている方には助けになる記事なのではないかと思います。 この記事の対象者 プログラミング初学者 Ruby on Railsは一通り学習し、アプリケーションを作りきった。 JavaScriptは多少触ったことあるかな程度 とにかくVue.jsを入れたい お金はケチりたい 学習ロードアップ 1. まずは公式! やはり最初は公式。 日本語で書かれているのでとてもわかりやすいですし、全ての基礎知識となりますので、やっときましょう。 (自分のアプリ内で応用する時に基礎知識があることが前提となるからです。) 個人的には、「コンポーネントの詳細」の章まで一通りやってみるのかいいかと思います。そのあとは必要に応じてやっていくのが効率が良いかと。 Vue公式 2. ここからはQiita記事 まずはVue.jsのアプリ立ち上げ つくづくQiitaの情報量には感服いたします。 まずはサクッとVue.jsアプリ立ち上げ。 2-1 立ち上げ~vue-router こちらの記事を参考にしていただくと簡単にアプリ立ち上げができます。 またこの記事内ではSPA作成の基本となるvue-routerについて学べます! 2-2 モダンなツールを学ぶ こちらではより見た目をかっこよくできるライブラリなどを学ぶことができます。 vue-typerとかsmooth-scrollとか今っぽくてかっこいいですよね〜 2-3 RailsにVueをいれる方法を学ぶ こちらにはRailsをバックエンドに、Vueをフロントエンドに使ったアプリを作る方法が記載されています。 すでにRailsアプリがあって、追加でVueを入れたい!という場合には「Vue.jsでHello Vue!を表示する」という章から読んでいくのがいいかなと思います。 ここまでで私が作ったレベルのアプリならもうできてしまいますよ! ちなみに私はここまでの学習の副産物としてポートフォリオサイトを作りました。ここでは見やすさの観点からvue-routerは使っていないですが、挙動はそれなりに今っぽい仕上がりになっているのではないかと思います。 3.Railsデータとの連携(筆者積み残し) 本当はRailsのデータと連携して、一つの機能をSPA化できたら尚良しと思っていました。 結論、devise特有のヘルパーなどのVueでの使い方など難しい点が多々あり、今後の積み残しにしていますが、 一度トライはしてみたので、その際に学習した内容をお伝えします。 3-1 いいね機能を作る こちらではRailsをバックエンド、Vueをフロントに使っていいね機能を実装しています。 ここでデータの渡し方などを学ぶことができます。 メモアプリを作る(有料) ここまで公式やQiitaの記事でやってきましたが、だんだん高度になってきたこともあり、情報が複雑化してきました。 そのため私はTechpitの記事を参考に、RailsとVueを使ったメモアプリ作成ハンズオン講座を買いました。 教材はこちら こう言ったベーシックな組み合わせ技術から初めて、高度なSPAを作り込んでいくのが良いのかなと思います。 4.まとめ 正直、あまり高度なことをやれるようになるわけではありません。 しかしVueを使えるようになること、Railsに組み込むことはこの記事の学習ロードマップに沿ってやっていくことで実現できると思います。 ご意見などありましたらコメントいただけると幸いです! 5.出典 Vue.js公式 https://v3.ja.vuejs.org/ (Qiita記事)【超簡単】Vue.jsを使って3時間で自分のホームページを作成&公開する https://qiita.com/abouch/items/35d7a202f3e74c7d4c3e (Qiita記事)Vue.jsでポートフォリオを書く https://qiita.com/shoma3571/items/025a4e8aedeb62b1fed4 (Qiita記事)Ruby on Rails, Vue.js で始めるモダン WEB アプリケーション入門 https://qiita.com/tatsurou313/items/4f18c0d4d231e2fb55f4 (Qiita記事)Rails + Vue.jsでいいね機能を実装する https://qiita.com/TakeshiFukushima/items/a6c698fec648c11eee9a (Techpit)Rails × Vue.js でメモアプリを作成しながらモダンな開発を学ぼう! https://www.techpit.jp/courses/123
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

再Vueディレクティブ学習

Vue.jsを薄く学んでいた際、v-bind、v-if、v-show、v-forなどはよく使っていました。 しかし、しっかり勉強しようとした際、知らないディレクティブが結構あったので、メモとして残します。 動作確認バージョン:2.6.12 v-once 最初の一度だけテンプレートを評価し、その後は静的なコンテンツとする。 例 v-onceが設定されているpタグは、ボタンを押してもhogeと表示されたままとなる。 data() { return { message: 'hoge' } }, methods: { onClick: function() { this.message = 'click'; } } <p>{{ message }}</p> <p v-once>{{ message }}</p> <button v-on:click="onClick">Click</button> 主な用途 更新不要な部分を評価させず、描画更新のパフォーマンスを上げる。 注意点 ノード内の全てのバインディングに影響するが、子孫ノードには適用されず、評価&更新される。 v-pre ノードと子孫ノードのコンパイルをスキップする。 例 v-preが設定されているpタグは、{{ message }}がそのまま表示される。 data() { return { message: 'hoge' } } <p>{{ message }}</p> <p v-pre>{{ message }}</p> 主な用途 ディレクティブが存在しないノードを評価させず、描画のパフォーマンスを上げる。 XSS対策。 v-cloak 関連づけられたviewModelのコンパイルが完了するまで、ノードにプロパティが残る。 例 コンパイル完了まで<p v-cloak>となり、完了後は<p>となる。 v-cloakプロパティが残存する間、cssの[v-cloak]が効いているため、pタグは表示されない。 data() { return { message: 'hoge' } } <p v-cloak>{{ message }}</p> [v-cloak] { display: none; } 主な用途 コンパイルに時間がかかる場合のチラつき防止など。 v-text ノードのtextContentに指定したデータで更新する。 => マスタッシュ構文と同じことをする。 本家にも 内部的には、{{ Mustache }} 挿入も textNode 上の v-text ディレクティブとしてコンパイルされます。 と書いてある。 例 v-textで指定したhogeがマスタッシュ構文の場合と同様に表示される。 <p v-text="message"></p> {{ message }} 主な用途 あまりわからず、、 v-html ノードのinnterHTMLを更新する。 => html形式の文字列をテキストではなく、プレーンなhtmlとして評価する。 例 pタグのinnterHTMLにデータオブジェクトのhtmlを設定している。 data() { return { html: '<span style="color: blue;">blue</span><br/><span style="color: red;">red</span>' } } <p v-html="html"></p> 主な用途 サーバーサイドからのレスポンス(html形式の)をそのまま表示する場合など。 注意点 XSSの危険性があるため、信頼できるデータソースからのデータのみに使用する。 入力フォームなどに使うのはNG。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む