20200513のvue.jsに関する記事は8件です。

DockerとVueとNginxで動いたから残しておく。

注意点

色々試してみて、やっと動いたレベルで詳しくはないです。
一つの朗報としては、難しい言葉は一切ありません。(知らないだけ。)

今回はDocker-composeと、Vueとnginxの三つを使って色々作成しました。

フォルダ構成

スクリーンショット 2020-05-13 22.38.11.png

フォルダ構成の注目ポイントは
Dockerで使うフォルダはcomposeフォルダに配置していること。
またlocal.ymlとproduction.ymlでローカル用とマジモン用でcompose upするファイルを分けているということ。

Vue

Vueで使用するファイルは、Vue createでほとんど作ったまま。
(vuetifyを入れたくらい。)

Dockerfile
FROM node:14.2.0-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 8080

nginxの設定

nginxのポイントは、Entrykitを使っているのがポイント。
メリットは、環境変数を指定してそこから設定ファイル.confを作成してくれる。
今回は、production.ymlで環境変数を指定して、
DockerfileENTRYPOINTでファイルの生成を指定している。
例: upstream.conf.tmplupstream.conf

なのでdocker-compose upで起動して
dockerの中を見るとconfファイルが生成されている。

{{ var "BACKEND_HOST" }}などは、環境変数でproduction.ymlで指定した値が入ってくる。

Dockerfile
FROM nginx:1.17.10

RUN apt-get update
RUN apt-get install -y wget
RUN wget https://github.com/progrium/entrykit/releases/download/v0.4.0/entrykit_0.4.0_linux_x86_64.tgz
RUN tar -xvzf entrykit_0.4.0_linux_x86_64.tgz
RUN rm entrykit_0.4.0_linux_x86_64.tgz
RUN mv entrykit /usr/local/bin/
RUN entrykit --symlink

RUN rm /etc/nginx/conf.d/*
COPY /compose/production/nginx/etc/nginx/nginx.conf.tmpl /etc/nginx/
COPY /compose/production/nginx/etc/nginx/conf.d/* /etc/nginx/conf.d/


ENTRYPOINT [ \
    "render", \
    "/etc/nginx/nginx.conf", \
    "--", \
    "render", \
    "/etc/nginx/conf.d/upstream.conf", \
    "--", \
    "render", \
    "/etc/nginx/conf.d/public.conf", \
    "--" \
    ]

CMD ["nginx", "-g", "daemon off;"]
log.conf
log_format json '{'
                 '"time":"$time_iso8601",'
                 '"remote_addr":"$remote_addr",'
                 '"request":"$request",'
                 '"request_method":"$request_method",'
                 '"request_length":"$request_length",'
                 '"request_uri":"$request_uri",'
                 '"uri":"$uri",'
                 '"query_string":"$query_string",'
                 '"status":"$status",'
                 '"bytes_sent":"$bytes_sent",'
                 '"body_bytes_sent":"$body_bytes_sent",'
                 '"referer":"$http_referer",'
                 '"useragent":"$http_user_agent",'
                 '"forwardedfor":"$http_x_forwarded_for",'
                 '"request_time":"$request_time",'
                 '"upstream_response_time":"$upstream_response_time"'
                '}';
public.conf.tmpl
server {
    listen {{ var "SERVER_PORT" | default "80" }} default_server;
    server_name {{ var "SERVER_NAME" | default "localhost" }};
    charset utf-8;

    location / {
        proxy_pass http://backend;
        proxy_pass_request_headers on;
        proxy_set_header host $host;
        {{ if var "LOG_STDOUT" }}
        access_log  /dev/stdout json;
        error_log   /dev/stderr;
        {{ else }}
        access_log  /var/log/nginx/backend_access.log json;
        error_log   /var/log/nginx/backend_error.log;
        {{ end }}
    }
}
upstream.conf.tmpl
upstream backend {
    server {{ var "BACKEND_HOST" }} max_fails={{ var "BACKEND_MAX_FAILS" | default 3 }} fail_timeout={{ var "BACKEND_FAIL_TIMEOUT" | default "10s" }};
}
nginx.conf.tmpl
user  nginx;
worker_processes {{ var "WORKER_PROCESSES" | default "1" }};

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  {{ var "WORKER_CONNECTIONS" | default "1024" }};
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  {{ var "KEEPALIVE_TIMEOUT" | default "65" }};

    gzip  {{ var "GZIP" | default "on" }};

    include /etc/nginx/conf.d/*.conf;
}

Docker-compose

ここで私は詰まったポイントがある。
BACKEND_HOSTに値を入れても入れても502 Bad Gatewayが出て苦しめられた。
BACKEND_HOSTは、nginxからどこにアクセスするかを指定する。

production.yml
version: "3"
services:
  nginx:
    build:
      context: .
      dockerfile: ./compose/production/nginx/Dockerfile
    container_name: ajiki_nginx
    depends_on:
      - vue
    environment:
      SERVICE_PORTS: 80
      WORKER_PROCESSES: 2
      WORKER_CONNECTIONS: 1024
      KEEPALIVE_TIMEOUT: 65
      GZIP: "on"
      BACKEND_HOST: ajiki_vue:8080 # container_name:port
      BACKEND_MAX_FAILES: 3
      BACKEND_FAIL_TIMEOUT: 10s
      SERVER_PORT: 80
      SERVER_NAME: localhost
      LOG_STDOUT: "true"
    ports:
      - "80:80"
    networks:
      - ajiki_client
    volumes:
      - assets:/var/www/_vue

  vue:
    build:
      context: .
      dockerfile: ./compose/production/vue/Dockerfile
    image: ajiki_vue
    container_name: ajiki_vue
    command: npm run serve
    networks:
      - ajiki_client
    volumes:
      - assets:/ajiki_vue/.vue/dist
    privileged: true
    stdin_open: true
    tty: true

networks:
  ajiki_client:
    external: true

volumes:
  assets:
    driver: local

使いそうなコマンド

コマンド
# Dockerネットワーク作成
docker network create <name>
# ビルド
docker-compose -f production.yml build
# 起動 -d でバックグラウンド
docker-compose -f production.yml up
# コンテナ一覧
docker container ls 
# コンテナに入る alpineはash
docker exec -it <container_name> bash

http://localhost/ にアクセスして画面が表示されたら成功。

最後に

まだよくわからないんですが、BACKEND_HOSTのホストにremoteaddrのホスト名を指定しても動きました。
BACKEND_HOST: 172.20.0.1:8080 なぜですか。。。

ぜひ、補足や付け足した方がいいことなどあればリクエストください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuejs+TypeScript×Firestoreで、追加,削除,更新を行う方法メモ

概要

Vuejs+TypeScriptを使って、Firestoreを扱うときのメモ

ドキュメント構成

Firestoreで下記のdocを更新したいときの方法

フィールド論理名 フィールド物理名
予算名 name string
予算額 cache number
色情報 color string

画面

新規作成と編集&削除を行う画面は下の感じで作成しました。

新規作成画面

image.png

編集&削除

image.png

画面ソース

Vue.jsでのdataの宣言

/view/plan/edit.vue
export default class PalnEdit extends Vue{

  private plan: Plan = { id : "" , name : "" , cache : 0 , color : "#" + Math.floor(Math.random() * 16777215).toString(16) }

}

データベースに合わせたTypeScriptでの方を定義

Plan.d.ts
export interface Plan {
  id?: string;
  name?: string;
  cache?: number;
  color?: string;
}

画面での値はv-modeより受け取る

<b-form-group class="form-group">
  <b-form-input
   type="number"
   placeholder="必須項目"
   v-model="plan.cache"
   :state="validated ? valid : null"
  />
   <b-form-invalid-feedback>{{ errors[0] }}</b-form-invalid-feedback>
</b-form-group>

登録や更新削除ボタンに@click:add()等のイベントも追加しておく

各処理のソース

追加

登録ボタンのイベントを定義

/view/plan/edit.vue
private add(){
    delete this.plan.id;
    firebase
      .firestore()
      .collection("plan")
      .add(this.plan)
      .then(function() {
      })
      .catch(function(error) {
      });
  }

更新

更新ボタンのイベントを定義
ドキュメントのidはurlパスから取得するように設定した。

/view/plan/edit.vue
private upd(){
      const docid = this.$route.params.id;
      const paln: Plan = this.plan;
      delete paln.id;
      firebase
        .firestore()
        .collection("plan")
        .doc(docid)
        .update(paln)
        .then(() => {

        })
        .catch(function(error) {
          console.error("Error adding document: ", error);
        }

削除

削除ボタンのイベントを定義
ドキュメントのidはurlパスから取得するように設定した。

/view/plan/edit.vue
  private del(){
    this.busy = true;
    const groupid = localStorage.groupid;
    const docid = this.$route.params.id;

    firebase
      .firestore()
      .collection("plan")
      .doc(docid)
      .delete()
      .then(function() {
      })
      .catch(function(error) {
        console.error("Error removing document: ", error);
      });
  }

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue/max-attributes-per-lineがprettierでうまくフォーマットされなかった

Vue.js を eslint とprettierを使って開発してたらフォーマットがうまくいかなかったので解決策と設定をメモ
エディタはvscode

問題

eslint-plugin-vueのルールmax-attributes-per-line(1行あたり属性値の最大数を制限するルール)をprettierがうまくフォーマットしてくれなかった

解決

issueのコメントに対策があった
https://github.com/prettier/eslint-plugin-prettier/issues/94

https://github.com/prettier/eslint-plugin-prettier/issues/94#issuecomment-438781791

結局、属性値の最大数を増やして対応が良さそう
複数の属性値が許可されてしまうが、1行あたりの文字数の制限超えたらよしなに改行してくれるので酷くならないよねって感じ
根本的な解決ではないけど、それで良いやと思ったので設定を変える

.eslintrc.json (落ち着いたやつ)

{
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "parser": "vue-eslint-parser",
  "parserOptions": {
    "parser": "babel-eslint",
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "plugins": [
    "vue",
    "prettier"
  ],
  "extends": [
    "eslint:recommended",
    "plugin:vue/recommended",
    "plugin:prettier/recommended",
    "prettier/vue"
  ],
  "rules": {
    "prettier/prettier": [
      "error",
      {
        "singleQuote": true,
        "htmlWhitespaceSensitivity": "ignore"
      }
    ],
    "vue/max-attributes-per-line": [
      "error",
      {
        "singleline": 5,
        "multiline": {
          "max": 1,
          "allowFirstLine": false
        }
      }
    ]
  }
}

その他

vscodeの拡張として入れてるprettierがフォーマットしてたり、veturがフォーマットしてた?のでvscodeの設定も少しいじった
(いじりすぎて迷子になったので後で見直す)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue + ESLint + Prettier 設定 自分メモ

Vue.js を eslint とprettierを使って開発してたらフォーマットがうまくいかなかったので解決策と設定をメモ
エディタはvscode

問題

eslint-plugin-vueのルールmax-attributes-per-line(1行あたり属性値の最大数を制限するルール)をprettierがうまくフォーマットしてくれなかった

解決

issueのコメントに対策があった
https://github.com/prettier/eslint-plugin-prettier/issues/94

https://github.com/prettier/eslint-plugin-prettier/issues/94#issuecomment-438781791

結局、属性値の最大数を増やして対応が良さそう
複数の属性値が許可されてしまうが、1行あたりの文字数の制限超えたらよしなに改行してくれるので酷くならないよねって感じ
根本的な解決ではないけど、それで良いやと思ったので設定を変える

.eslintrc.json (落ち着いたやつ)

{
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "parser": "vue-eslint-parser",
  "parserOptions": {
    "parser": "babel-eslint",
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "plugins": [
    "vue",
    "prettier"
  ],
  "extends": [
    "eslint:recommended",
    "plugin:vue/recommended",
    "plugin:prettier/recommended",
    "prettier/vue"
  ],
  "rules": {
    "prettier/prettier": [
      "error",
      {
        "singleQuote": true,
        "htmlWhitespaceSensitivity": "ignore"
      }
    ],
    "vue/max-attributes-per-line": [
      "error",
      {
        "singleline": 5,
        "multiline": {
          "max": 1,
          "allowFirstLine": false
        }
      }
    ]
  }
}

その他

vscodeの拡張として入れてるprettierがフォーマットしてたり、veturがフォーマットしてた?のでvscodeの設定も少しいじった
(いじりすぎて迷子になったので後で見直す)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Go】脱Go初心者のためにGoでオンライン交流会に役立つツールを制作してみた

はじめに

Go案件に携わりたいなと思い、お勉強のがてらこんなツールを作りました。
制作過程をまとめようと思っていたんですけど、つまづいたポイントをまとめただけになりました。。
合間を見てどんどん機能を追加していこうと思います!

・デモGIF
gif.gif

・URL
https://group-shuffle-app.herokuapp.com/

・Github
https://github.com/yagi-eng/group_shuffle_gui

ツールの説明

以下の問題解決を目的としたツールです。

  • 交流会などで、複数回参加者をシャッフルしてグループ分けする
  • なるべく重複しないようにする

おおまかな流れ

  1. 参加者の組み分けをランダムに生成し、以下の評価方法に基づき評価
  2. 1.を複数回繰り返し、一番評価が良い組み分けを最良の組み分けとする

評価方法

  • 組み分けに対して、参加者毎のスコアを以下のように定義
    • 各回で同席した人との同席回数の合計を、全ての回で合計した値
  • 全参加者のスコアの標準偏差が最も小さい組み分けを最良の組み合わせとする

例えば、1さんが1回目に[2,3,4]、2回目に[4,5,6]の人と同席になった場合。
1回目では、いずれも初同席なので同席回数はそれぞれ1となり、1さんのスコアは 1+1+1=3
2回目では、4とは2回目の同席なので同席回数は2となり、1さんのスコアは 2+1+1=4

使用技術

  • Go 1.14
  • echo v3.3.10
  • Heroku
  • Vue.js

Goでつまづいたこと

ディレクトリ構成のベストプラクティスがわからない

以下の記事を参考にしました。

package分割がうまくできない

以下の記事を参考にしました。
Go Modules でインターネット上のレポジトリにはないローカルパッケージを import する方法

go moduleを使うと実現できます。
import文に相対パスを指定する方法もありますが、現在では非推奨なようです。

package化したメソッドの呼び出しは (package名).(メソッド名)である必要があります。
当たり前のことなんですけど、これをうっかり見落としていて少し時間をかけてしまいました。

go module導入したらechoがimportされなくなった

以下の記事を参考にしました。
GolangとEchoでお手軽にAPIサーバを立てる

$ go mod init

した後に

$ go get github.com/labstack/echo/v4

すると、いい感じに解消してくれます。
自分の場合は順番が逆でした。

ちなみに、以下のコマンドで、 go.modファイルから不要なモジュールを削除してくれます。

$ go mod tidy

型が違うだけの処理を共通化できない

具体的に、 util/slice/slice.go の以下2つのメソッドを1つにまとめたかったができなかった。

slice.go
// DevideArr スライスを分割する
func DevideArr(arr []int, lenOfEachSlice int) [][]int {
    arrs := [][]int{}
    len := len(arr)

    for i := 0; i < len; i += lenOfEachSlice {
        end := i + lenOfEachSlice
        if len < end {
            end = len
        }
        arrs = append(arrs, arr[i:end])
    }

    return arrs
}

// DevideArrStr スライスを分割する
func DevideArrStr(arr []string, lenOfEachSlice int) [][]string {
    arrs := [][]string{}
    len := len(arr)

    for i := 0; i < len; i += lenOfEachSlice {
        end := i + lenOfEachSlice
        if len < end {
            end = len
        }
        arrs = append(arrs, arr[i:end])
    }

    return arrs
}

interface{} を使えばできるかもと思ったが、[]interface{} を引数として、[]int を受け取ろうとしたらコンパイルエラーになった。

参考
Go言語の型とreflect

Herokuにデプロイはできるが動作しない

以下のコミットのようにポート番号を環境変数から取得するように修正します。
https://github.com/yagi-eng/group_shuffle_gui/pull/4/commits/6ad3ee022d4990b8459c0141b2905278c3259eef

server.go
-   e.Logger.Fatal(e.Start(":1323"))
+   e.Logger.Fatal(e.Start(":" + os.Getenv("PORT")))

Vueでチャレンジしたこと

formのvalidation

以下の記事を参考にしました。
これでわかるvue.jsのフォームバリデーションVuelidate
段階的に実装していくのでわかりやすかったです。

axiosを使ったAPI通信

以下の記事を参考にしました。
axiosの使い方まとめ (GET/POST/例外処理)
Vueではないけど、初めて使いました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vetur 2339] [E] Property '$store' does not exist on type 'CombinedVueInstance<Vue, unknown, ...

症状

以下のように this には $store は無いよ、と怒られます。

import Vue from 'vue'
export default Vue.extend({
  methods: {
    hello () {
      const value = this.$store.getters.value
      //                 ^^^^^^
      // [Vetur 2339] [E] Property '$store' does not exist on type 
      // 'CombinedVueInstance<Vue, unknown, ...
    }
  }
})

対応

以下の記事を参考にさせていただいて、妥協してワーニングを切りました。

tsconfig.json
{  
  "compilerOptions": {  
    "noImplicitAny": false, 
    "noImplicitThis": false,  
  }, 
}  

検討

本来は以下の記事を参考にさせていただいて、Vuex にも型定義をつけたかったのですが、いまは妥協しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue】見た目とロジックを分ける

この記事は、主に下記の記事を参考にしています。
Vueを用いた開発プロジェクト用に「コンポーネント設計・実装ガイドライン」を作った話 - Qiita
Vue使いなら知っておきたいVueのパターン・小技集 - Qiita

ContainerとPresenter

もともとはReactから派生してきた考え方です。
下記記事がわかりやすいです。
[redux] Presentational / Container componentの分離 - react-redux.connect()のつかいかた - Qiita
ComponentとContainerについて - Qiita
Presentational and Container Components - Dan Abramov - Medium

ContainerとPresenterを分けることのメリット

最初はなんでわざわざ分けるのかと不思議でしたが、下記のメリットが大きいのかなと思いました。

同じPresentational Componentに異なる状態ソースを持たせることで、それらを別のContainer Componentsに再利用することができるようになる
ComponentとContainerについて - Qiita より引用

似たようなPresentational Componentが増えてきたらかなり恩恵を受けられるのではないでしょうか。

実装してみる

全体像

  • container がロジック、presenter が UI
  • それぞれのcomponentがに、container, presenterファイルが生成される
  • containerpresenterwrapする
  • componentを呼び出す時は、containerを呼び出す

Container

特徴

・データや振る舞いに責務を持つ
・データや振る舞いをPresentational Componentや他のContainer Componentに提供する
・VuexのstoreやVue Routerのrouteを参照しても良い(しなくても良い)
・通常、DOMのマークアップやCSSスタイルを持たない。(仮にDOMを持つとしても、それはラッパー用のdivタグなど)
Vueを用いた開発プロジェクト用に「コンポーネント設計・実装ガイドライン」を作った話 - Qiita より引用

presenterを読み込む

// presenterの読み込み
// ここでcontainer が presenter を wrap することになる
import SampleModal from './presenter'

// connect 高階関数
const connect = (SampleModal) => {
  return {
    name: `${SampleModal.name}Container`,
    methods: {
      handleLogin(social) {
        this.$auth.loginWith(social)
      }
    },
    render(h) {
      return h(SampleModal, {
        props: {
          // propsとしてmethod, 
          handleLogin: this.handleLogin
        }
      })
    }
  }
}

export default connect(SampleModal)

高階関数 connect

connectが、ContainerとPresenterを結びつけるキーとなっています。
connectは、高階関数で、mapとかと同じですね。
JavaScript 高階関数を説明するよ - Qiita

以下のconnectは、Presentational Componentを引数に取り、そのコンポーネントが関心を持つ、
VuexのmoduleのデータとVue Routerのメソッドへのアクセスを与えたContainer Componentを返す高階関数
Vueを用いた開発プロジェクト用に「コンポーネント設計・実装ガイドライン」を作った話 - Qiita より引用

containrの呼び出し

com@ponentを使用する時は、containerを呼び出して使用します。

import SampleModal from '@/components/common/Sample/container'

Presenter

特徴

  • 見た目に責務を持つ
  • コンポーネント自身の状態はほとんど持たない
  • propsとしてデータとコールバックを受け取れる
  • アクションやストアに依存しない

実装

<script>
export default {
  name: 'SampleModal',
  // props としてuketoru
  props: {
    handleLogin: {
      type: Function,
      default: () => {}
    }
  },
  render(h, context) {
    return (
      <modal
        name="sign-in-modal"
        resizable={true}
        adaptive={true}
        scrollable={true}
        width={this.$device.isMobile ? '90%' : '40%'}
        height="auto"
      >
        <div class="modal-sign-in">
          <p class="modal-sign-in-word">Sign In</p>
          <div class="modal-sign-in-button">
            <button
              class="modal-sign-in-facebook"
              onClick={() => {
                this.handleLogin('facebook')
              }}
            >
              <font-awesome-icon
                icon={['fab', 'facebook']}
                class="modal-sign-in-facebook-font"
              />
              <span class="modal-sign-in-facebook-word">
                Sign In with Facebook
              </span>
            </button>
          </div>
        </div>
      </modal>
    )
  }
}
</script>
// sampleなので、CSSは適当
<style scoped lang="scss">
@import '~assets/scss/variables';
.modal-sign-in {
  padding: 10px;
  .modal-sign-in-word {
    margin: 10px;
  }
  .modal-sign-in-button {
    margin: 5px;
    .modal-sign-in-facebook {
      max-width: 250px;
      .modal-sign-in-facebook-font {
        margin-right: 10px;
      }
      .modal-sign-in-facebook-word {
        font-weight: bold;
      }
    }
  }
}
</style>


その他参考にした記事

export defaultについて - Qiita

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue Test Utils カスタムコンポーネントでv-modelを適用したときのテスト

Vue Test Utils カスタムコンポーネントでv-modelを適用したときのテスト

やりたいこと

以下のようなカスタムコンポーネントにv-modelを適用したときに入力値をdataに反映させてテストをする。

sample.vue
<template>
  <div>
    <formInput v-model="test" type="text"></formInput>
  </div>
</template>

<script>
import formInput from '~/components/formInput.vue'

export default {
  components: { formInput },
  data() {
    return {
      test: ''
    }
  }
}
</script>

NG例

input要素だとドキュメントにある通り、以下のようにdataに値を反映することができる。
しかし、上記のようなカスタムコンポーネントでこのコードを実行すると"wrapper.setValue() cannot be called on this element"のようなエラーが表示される。

sample.spec.js
import { shallowMount } from '@vue/test-utils'
import Foo from '~/pages/sample.vue'

const wrapper = shallowMount(Foo)

describe('input要素だとこれでパスする', () => {
  test('テストする', () => {
    const textInput = wrapper.find('[type="text"]')
    textInput.element.value = 'some value'

    expect(wrapper.vm.test).toBe('some value')
  })
})

解決策

inputイベントを発火させることで、入力値をdataに反映させてテストすることが可能。

sample.spec.js
import { shallowMount } from '@vue/test-utils'
import Foo from '~/pages/sample.vue'

const wrapper = shallowMount(Foo)

describe('カスタムコンポーネントのv-modelでもこれでパスする', () => {
  test('テストする', () => {
    const textInput = wrapper.find('[type="text"]')
    textInput.vm.$emit('input', 'some value')

    expect(wrapper.vm.test).toBe('some value')
  })
})

参考

こちらにある通り、v-modelはv-onとv-bindを1行で表現したものなので、イベントを発火することでdataを更新することができます。

<input v-model="searchText">
<input :value="searchText" @input="searchText = $event.target.value">

誰かの役に立つことを願って。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む