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

【Nuxt.js】v-data-tableにリンクを埋め込む

v-data-table内ではNuxt-linkがうまく機能しなくて困っていましたが、
v-btnに「:to=」でリンクを仕込めました。
アイコン型のリンクする場合、v-btnの中にv-iconを組み込む。

<v-data-table
 :headers="headers"
 :items="指定したいテーブル"
  item-key="id"
>
  <template v-slot:[`item.actions`]="{ item }">
    <v-btn
      icon
      :to="'/任意のリンク' + item.id"
      nuxt    
     >
      <v-icon dark>
        mdi-pencil
       </v-icon>
    </v-btn>
  </template>
</v-data-table>

image.png

◆header部

headers() {
        return [ 
      ※他の項目は省略しています
             {
            text: '操作',
            value: 'actions',
            sortable:false,
             }
          ]}

◆バージョン情報
 "nuxt": "^2.0.0"
 "vue": "^2.6.12"
 "@nuxtjs/vuetify": "^1.11.2"

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

【Vue.js】基礎知識

はじめに

現在、エンジニア転職活動中です。本日から、Vue.jsの学習を始めましたので、アウトプットとして記述していきます。

なぜVue.js?

なぜ、Vue.jsの学習を始めたのかを記載します。

・転職活動用に作成しているポートフォリオが、HTML,CSSとBootstrapの多用により、サイトに動きがないため。
・GoogleTrendsにて比較を見た結果、Vue.jsの比率が高く、今後入社した際にも、Vue.jsを採用されているまたは今後採用する予定の企業も多いと感じたため(https://trends.google.com/trends/explore?date=today%205-y&q=vue.js,react.js,angular.js)
・これまでRuby,Ruby on Railsの学習に注力しており、フロントエンドの知識があまりなかったため

以上の理由により、Vue.jsの学習を初めていきたいと思いました。

導入方法

Vue.jsの導入はダウンロードまたはCDNを利用する方法がありました。
今回はCDNを利用してVue.jsを読み込むことで、利用していきます。

index.html
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

基本機能

テキストのバインディング

index.html
<div id="app">
  <p>{{ message }}</p>
</div>
practice.js
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!'
  }
})

繰り返し

index.html
<div id="app">
  <ul>
    <li v-for="item in list">{{ item }}</li>
  </ul>
</div>
practice.js
var app = new Vue({
  el: '#app',
  data: {
    list: ['野球', 'サッカー', 'バスケットボール']
  }
})

イベント発火

index.html
<div id="app">
  <button v-on:click="handleClick">Click</button>
</div>
practice.js
var app = new Vue({
  el: '#app',
  methods: {
    handleClick: function(event) {
      alert(event.target)
    }
  }
})

Image from Gyazo

フォーム入力の同期化

index.html
<div id="app">
  <p>{{ message }}</p>
  <input v-model="message">
</div>
practice.js
var app = new Vue({
  el: '#app',
  data: {
    message: ''
  }
})

Image from Gyazo

条件分岐

index.html
<div id="app">
  <p v-if="show">Hello Vue.js!</p>
</div>
practice.js
var app = new Vue({
  el: '#app',
  data: {
    show: true
  }
})

Image from Gyazo

トランジションとアニメーション

index.html
<div id="app">
  <button v-on:click="show=!show">切り替え</button>
  <transition>
    <p v-if="show">Hello Vue.js!</p>
  </transition>
</div>
practice.js
var app = new Vue({
  el: '#app',
  data: {
    show: true
  }
})
practice.css
.v-enter-active, .v-leave-active {
  transition: opacity 1s;
}

.v-enter, .v-leave-to {
  opacity: 0;
}

Image from Gyazo

おわりに

本日学んだ基本知識です。明日以降構成の内容などアウトプットしていきたいと思います。

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

Laravel6 + Vue.js + Laravel SanctumのSPA認証でのアクセス制限の簡単な実装。

自分用なのでざっくりと。
時間がある時にもっと詳しい記事を書きたい。

例えばdashboardページへ未認証ユーザーがアクセスしようとするとログインページへリダイレクトさせたい場合。

routes/api.php
Route::middleware('auth:sanctum')->get('/authenticated', function () {
    return 'authenticated';
});
resources/js/app.js
import VueRouter from 'vue-router';
import DashBoard from "./components/DashBoard";
import LoginComponent from "./components/LoginComponent";

//〜略〜

const router = new VueRouter({
    mode: 'history',
    routes: [
        {
            path: '/login',
            name: 'login',
            component: LoginComponent,
        },
        {
            path: '/dashboard',
            name: 'dashboard',
            component: DashBoard,
            beforeEnter: (to, from, next) => {
                axios.get('/api/authenticated')
                    .then((res) => {
                        if (res.data == 'authenticated') {
                            next();
                        }
                    }).catch(() => {
                        next({ name: 'login' });
                    });
            },
        },
    ]
});

//〜略〜

このようにすれば良い。

おまけ

最初は、

routes/api.php
Route::middleware('auth:sanctum')->get('/authenticated', function () {
    return true;
});
resources/js/app.js
import VueRouter from 'vue-router';
import DashBoard from "./components/DashBoard";
import LoginComponent from "./components/LoginComponent";

//〜略〜

const router = new VueRouter({
    mode: 'history',
    routes: [
        {
            path: '/login',
            name: 'login',
            component: LoginComponent,
        },
        {
            path: '/dashboard',
            name: 'dashboard',
            component: DashBoard,
            beforeEnter: (to, from, next) => {
                axios.get('/api/authenticated')
                    .then(() => {
                        next();
                    }).catch(() => {
                        next({ name: 'login' });
                    });
            },
        },
    ]
});

//〜略〜

ってやってみて、実際にログインしてdashboardページにアクセスしようとしたら、

The Response content must be a string or object implementing __toString(), \"boolean\" given.

っていう「booleanじゃだめよ!」っていうエラーが出てうまくいかなかったんだよねえ。。

でもとある外国人のYouTube動画ではこのやり方でうまくいってたんだよねえ。。

どうしてなんだろう。
まだまだ勉強不足みたいです。。

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

Viteとは何なのか

はじめに

ちょっと前に話題になっていた Vite について、どのようなものなのか調べつつまとめてみました。

どのようなものなのか

従来のビルドツール(Vue CLI等)に比べて、高速で動作するビルドツールのようです。
公式サイトは下記。
https://vitejs.dev/

Vue.js を開発されたEvan You氏が開発されました。
リポジトリは下記。(2021/01/18段階では v2.0.0-beta.30 )
https://github.com/vitejs/vite
Vue.js の他、 React Preact のビルドもサポートしているようです。

  • 開発時はバンドルが不要なので、開発サーバーの起動が早い。
  • HMR(Hot Module Replacement)(画面の再描画なしに変更を適用してくれる機能)が、修正分のみを適応するため、
    モジュールの総数に関係なくかなり高速。

といった利点があるようです。
この辺りは下記ドキュメントにより詳細に書いてあります。
https://vitejs.dev/guide/features.html

導入はどのように行うのか

下記に手順があるので、そちらで行うことができそうです。
https://vitejs.dev/guide/

まず、下記コマンドを実行します。

yarn create @vitejs/app
  • (ここではプロジェクト名に vite-project を指定しています。)
% yarn create @vitejs/app
yarn create v1.22.4
[1/4] ?  Resolving packages...
[2/4] ?  Fetching packages...
[3/4] ?  Linking dependencies...
[4/4] ?  Building fresh packages...
success Installed "@vitejs/create-app@1.2.0" with binaries:
      - create-app
      - cva
✔ Project name: · vite-project
Scaffolding project in /Users/hogehoge/workspace/vite/vite-project...
✔ Select a template: · vue-ts

Done. Now run:

  cd vite-project
  npm install (or `yarn`)
  npm run dev (or `yarn dev`)

✨  Done in 66.19s.

手順に沿って、下記を順に行なっていきます。

  cd vite-project
  npm install (or `yarn`)
  npm run dev (or `yarn dev`)
  • モジュールのインストール
% yarn
yarn install v1.22.4
warning package.json: No license field
info No lockfile found.
warning vite-project@0.0.0: No license field
[1/4] ?  Resolving packages...
warning vite > fsevents@2.1.3: "Please update to latest v2.3 or v2.2"
warning vite > rollup > fsevents@2.1.3: "Please update to latest v2.3 or v2.2"
[2/4] ?  Fetching packages...
[3/4] ?  Linking dependencies...
[4/4] ?  Building fresh packages...
success Saved lockfile.
✨  Done in 18.80s.
  • サーバーの立ち上げ
% yarn dev
  Vite dev server running at:

  > Local:    http://localhost:3000/
  > Network:  http://XX.XXX.XXX.XX:3000/
  > Network:  http://XX.XXX.XXX.XX:3000/

  ready in 1319ms.

毎度お馴染みの http://localhost:3000/ にアクセスすると、下記が表示されます。
スクリーンショット 2021-01-18 11.25.10.png

その他情報

  • Typescript のサポートも行っているようですが、型チェックは行ってくれないようです。
  • 内部のトランスパイルには、 esbuild を使用しているそう。
    この辺りの情報も公式に載っているので、ご興味のある方は是非。
    https://vitejs.dev/guide/features.html#npm-dependency-resolving

終わりに

チュートリアルで行ったようなプロジェクトでは、モジュール数も少ないため速度的にはわかりませんでしたが、
実プロジェクトで用いた時にどのような速度が出るかが気になるところです。
開発環境のサーバー起動、ビルド時間で悩みを抱えている方には、有益なツールなのではないでしょうか。
開発されているのがEvan You氏とのことで、今後の発展が楽しみなツールです。

参考にした情報

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

困っている方必見!ページトップからある要素までの高さの取得方法

cssにてpositionを使っているときに、topleftなどを%で指定していると、使っている機種によって%の値が変わってきます。

これは厄介。。。

こんな時に役立つのが、Javascriptを使ってページトップからの高さを取得してしまえば、機種によって%の値を変える必要はありません!

結論から言うと、getBoundingClientRect()pageYOffsetの2つの関数を使います。

それでは使い方を見ていきましょう!!

要素を取得

index.js
const topToElement = document.getElementsByClassName('class-name')[0]

また、document.getElementIdなどで取得してください

ページトップからの高さを取得

index.js
const topToElement = document.getElementsByClassName('class-name')[0]
const topToElementHeight = topToElement.getBoundingClientRect().top + window.pageYOffset
console.log(topToElementHeight)

このようにして高さを取得することができます!

また、これらはVue.jsで使う場合、DOM要素と紐づけられた後のmountedで行ってください。

createdでは取得できないのでご了承ください。

以上、「ページトップからある要素までの高さの取得方法」でした!

良かったら、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

Thank you for reading

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

困っている方必見!offsetTopで高さが取得できない時の対処法

cssにてpositionを使っているときに、topleftなどを%で指定していると、使っている機種によって%の値が変わってきます。

これは厄介。。。

こんな時に役立つのが、Javascriptを使ってページトップからの高さを取得してしまえば、機種によって%の値を変える必要はありません!

結論から言うと、getBoundingClientRect()pageYOffsetの2つの関数を使います。

それでは使い方を見ていきましょう!!

※注意 要素の高さは普通offsetTopで取得できます。ただ、親要素にposition:relativeが設定してあると取得できない場合があるので、今回はそのやり方でやっていきます。

要素を取得

index.js
const topToElement = document.getElementsByClassName('class-name')[0]

また、document.getElementIdなどで取得してください

ページトップからの高さを取得

index.js
const topToElement = document.getElementsByClassName('class-name')[0]
const topToElementHeight = topToElement.getBoundingClientRect().top + window.pageYOffset
console.log(topToElementHeight)

このようにして高さを取得することができます!

また、これらはVue.jsで使う場合、DOM要素と紐づけられた後のmountedで行ってください。

createdでは取得できないのでご了承ください。

以上、「ページトップからある要素までの高さの取得方法」でした!

良かったら、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

Thank you for reading

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

一時的

main.vue
<template>
  <div class="hello">
  </div>
</template>

<script>
export default {
  data(){
    return {
      addMembers: [],
      renewMembers: [],
      oldMembers : 
      [
        {teamName: "team1", memberName: "tanaka", family: [{father: "namihei", sex: "man"}, {mothor: "hune", sex: "female"}], bio: {type: "A", tall: "small"}},
        {teamName: "team2", memberName: "yamada", family: [{father: "tusbasa", sex: "man"},{mothor: "shizuka", sex: "female"}], bio: {type: "B", tall: "small"}},
        {teamName: "team3", memberName: "ueno", family: [{father: "nobita", sex: "man"}, {mothor: "mama", sex: "female"}], bio: {type: "AB", tall: "tall"}},
      ],

      newMembers : 
      [
        {teamName: "team1", memberName: "tanaka", family: [{mothor: "hune", sex: "female"}, {father: "namihei", sex: "man"}], bio: {type: "A", tall: "small"}},
        {teamName: "team2", memberName: "yamada", family: [{father: "tusbasa", sex: "man"}, {mothor: "shizuka", sex: "female"}], bio: {type: "B", tall: "small"}},
        {teamName: "team3", memberName: "ueno", family: [{father: "nobita", sex: "man"}, {mothor: "mama", sex: "female"}], bio: {type: "AB", tall: "tall"}},
        {teamName: "team4", memberName: "uesugi", family: [{father: "hideo", sex: "man"}, {mothor: "kera", sex: "female"}], bio: {type: "AB", tall: "small"}}
      ]
    }
  },
  created(){
    this.mainProcess()
  },
  methods: {
    CheckObjectBio: function (newMember, oldMember) {
      // 変更前のメンバー情報を文字列に変換
      var strOldMember = JSON.stringify(oldMember);
      if (strOldMember.indexOf(JSON.stringify(newMember)) < 0) {
          return true
      } else {
          return false
      }
    },

     CheckObjectFamily: function(newMember, oldMember) {
      // 変更前のメンバー情報を文字列に変換
      var strOldMember = JSON.stringify(oldMember);
      // 変更後メンバーを文字列に変換して変更前メンバーとデータが一致するか確認
      var test = newMember.filter(function (item) {
          // 一致しないデータがあった場合はtureを返す
          if (strOldMember.indexOf(JSON.stringify(item)) < 0) {
              return true
          } else {
              return false
          }
      }).length > 0
      return test
    },

    compareMember: function(newMember, oldMember, renewList){
      if (newMember.memberName != oldMember.memberName){
          renewList.push([newMember, oldMember])
      }
      if (this.CheckObjectFamily(newMember.family, oldMember.family)){
          renewList.push([newMember, oldMember])
      }
      if (this.CheckObjectBio(newMember.bio, oldMember.bio)){
          renewList.push([newMember, oldMember])
      }
      return renewList
    },

    mainProcess: function(){
      for (let newMember of this.newMembers){
        newMember['newFlag'] = true
        loop:for (let oldMember of this.oldMembers){
          if (newMember.teamName == oldMember.teamName){
            newMember['newFlag'] = false
            // 更新比較処理
            this.renewMembers = this.compareMember(newMember, oldMember, this.renewMembers)
            break loop;
          }
        }
      }
      for (let newMember of this.newMembers){
        if (newMember['newFlag'] == true){
          this.addMembers.push(newMember)
        }
      }
      console.log(this.renewMembers)
      console.log(this.addMembers)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

え、意外とみんな知らない!?要素外でクリックしたときにイベントを発火させる方法

皆さんこんにちは!

今サイト制作を行っている際に、ふとドロップダウンメニュー(プルダウンとも呼ぶ)を作っていた時、「要素外でクリックしたときにメニューを閉じたいな~」と思い、1から構築しようとしたけどめちゃだるい!!!

僕がサイト制作を行うときに一番心掛けていることは、「どれだけ楽をして作るか」を日々考えながらやっています。

そんな僕にとって、この機能を1から作るなんて死んでもやりたくもない。。。

Googleで検索したところ、、、

なんとありました!!

パッケージ名は「vue-click-outside」

初めて聞いた

さて、今回はドロップダウンメニューを作りつつ「vue-click-outside」を使っていきたいと思います。

使い方だけ見たいという方は、こちらのサイトからご覧ください。

使い方の例も載っているので、分かりやすいかと。

それでは、順を追って一緒に使い方を見ていきましょう!

パッケージのインストール

npm i vue-click-outside --save-dev

ドロップダウンメニューの作成(リストの作成)

ここからはドロップダウンメニューを作っていきます。

表示したいメニュー項目をmenuListで定義し、isOpendで要素をクリックしたかを判断します。また、menuListのプロパティはお好みで設定してください。

App.vue
<script>
export default{
  data() {
    return {
      isOpend: false,
      menuList: [
        {
          icon: 'user',
          id: 'account?isActive=0',
          labelText: 'アカウント情報'
        },
        {
          icon: 'envelope',
          id: 'email?isActive=1',
          labelText: 'メールアドレス変更'
        },
        {
          icon: 'key',
          id: 'password?isActive=2',
          labelText: 'パスワード変更'
        },
        {
          icon: 'calendar-alt',
          id: 'event?isActive=3',
          labelText: 'イベント'
        },
        {
          icon: 'user-minus',
          id: 'withdrawal?isActive=4',
          labelText: '退会'
        }
      ]
    }
  },
}
</script>

v-forでメニューリストを表示

次は、先ほど作成したオブジェクトを使用し、v-forを使って表示していきます。

また、ドロップダウンメニューを表示したときモーダルウィンドウとして表示するため、Buefyを使っていきます。アイコンもBuefyで表示します。

Buefyの使い方は、こちら以下の記事で詳しく書いているので、興味のある方はぜひご覧ください!

初心者必見!サイト制作は楽してなんぼ。CSSフレームワークBuefyの紹介!!
効率的にサイト作り!Buefyでアイコンを表示しよう!!

App.vue
<template>
  <div class="phone-side-menu">
    <div class="drop-down-menu">
      <div class="drop-down-menu-wrapper">
        <b-modal v-model="isOpend">
          <div class="drop-down-menu-list">
            <ul>
              <li v-for="(element, index) in menuList" :key="index">
                <input :id="element.id" name="sideMenuItems" type="radio" />
                <label :for="element.id" class="phone-menu-label">
                  <b-icon pack="fas" :icon="element.icon" size="medium"></b-icon>
                  {{ element.labelText }}
                </label>
              </li>
            </ul>
          </div>
        </b-modal>
      </div>
    </div>
  </div>
</template>
<script>
export default{
  data() {
    return {
      isOpend: false,
      menuList: [
        {
          icon: 'user',
          id: 'account?isActive=0',
          labelText: 'アカウント情報'
        },
        {
          icon: 'envelope',
          id: 'email?isActive=1',
          labelText: 'メールアドレス変更'
        },
        {
          icon: 'key',
          id: 'password?isActive=2',
          labelText: 'パスワード変更'
        },
        {
          icon: 'calendar-alt',
          id: 'event?isActive=3',
          labelText: 'イベント'
        },
        {
          icon: 'user-minus',
          id: 'withdrawal?isActive=4',
          labelText: '退会'
        }
      ]
    }
  },
}
</script>

モーダルウィンドウの表示

次は、クリックされたときにモーダルウィンドウを表示する関数、ボタンを作ります。

App.vue
<template>
  <div class="phone-side-menu">
    <div class="drop-down-menu">
      <div class="drop-down-menu-wrapper">
        <!-- ここから追加  -->
        <b-button type="is-text" @click="toggleMenuList">
            ドロップダウンメニュー
        </b-button>
        <b-modal v-model="isOpend">
          <div class="drop-down-menu-list">
            <ul>
              <li v-for="(element, index) in menuList" :key="index">
                <input :id="element.id" name="sideMenuItems" type="radio" />
                <label :for="element.id" class="phone-menu-label">
                  <b-icon pack="fas" :icon="element.icon" size="medium"></b-icon>
                  {{ element.labelText }}
                </label>
              </li>
            </ul>
          </div>
        </b-modal>
      </div>
    </div>
  </div>
</template>
<script>
export default{
  data() {
    return {
      isOpend: false,
      menuList: [
        {
          icon: 'user',
          id: 'account?isActive=0',
          labelText: 'アカウント情報'
        },
        {
          icon: 'envelope',
          id: 'email?isActive=1',
          labelText: 'メールアドレス変更'
        },
        {
          icon: 'key',
          id: 'password?isActive=2',
          labelText: 'パスワード変更'
        },
        {
          icon: 'calendar-alt',
          id: 'event?isActive=3',
          labelText: 'イベント'
        },
        {
          icon: 'user-minus',
          id: 'withdrawal?isActive=4',
          labelText: '退会'
        }
      ]
    }
  },
  methods: {
    toggleMenuList() {
      this.isOpend = !this.isOpend
    }
}
</script>

ボタンをクリックしたら、toggleMenuListという関数を実行してモーダルウィンドウの表示・非表示を行います。

要素外をクリックしたときの関数を作成

最後に、モーダルウィンドウを表示中に要素外をクリックしたら、モーダルウィンドウを閉じるための関数を作成していきます。

関数名はhideMenuList、先ほどインストールしたパッケージをインストールしscript内でdirectivesを定義、v-click-outsideでイベントの発火

というような仕組みになっております。

App.vue
<template>
  <!-- イベント発火 -->
  <div v-click-outside="hideMenuList" class="phone-side-menu">
    <div class="drop-down-menu">
      <div class="drop-down-menu-wrapper">
        <!-- ここから追加  -->
        <b-button type="is-text" @click="toggleMenuList">
            ドロップダウンメニュー
        </b-button>
        <b-modal v-model="isOpend">
          <div class="drop-down-menu-list">
            <ul>
              <li v-for="(element, index) in menuList" :key="index">
                <input :id="element.id" name="sideMenuItems" type="radio" />
                <label :for="element.id" class="phone-menu-label">
                  <b-icon pack="fas" :icon="element.icon" size="medium"></b-icon>
                  {{ element.labelText }}
                </label>
              </li>
            </ul>
          </div>
        </b-modal>
      </div>
    </div>
  </div>
</template>
<script>
export default{
  // ディレクティブを定義
  directives: {
    ClickOutside
  },
  data() {
    return {
      isOpend: false,
      menuList: [
        {
          icon: 'user',
          id: 'account?isActive=0',
          labelText: 'アカウント情報'
        },
        {
          icon: 'envelope',
          id: 'email?isActive=1',
          labelText: 'メールアドレス変更'
        },
        {
          icon: 'key',
          id: 'password?isActive=2',
          labelText: 'パスワード変更'
        },
        {
          icon: 'calendar-alt',
          id: 'event?isActive=3',
          labelText: 'イベント'
        },
        {
          icon: 'user-minus',
          id: 'withdrawal?isActive=4',
          labelText: '退会'
        }
      ]
    }
  },
  methods: {
    toggleMenuList() {
      this.isOpend = !this.isOpend
    },
    // 関数を定義
    hideMenuList() {
      this.isOpend = false
    }
}
</script>

いかがだったでしょうか?

このようにして、ドロップダウンメニューのモーダルウィンドウを表示、要素外をクリックしたらモーダルウィンドウを閉じるという一連の動作を完成させることができます!

最近、モチベが低下しつつある。。。

それでも僕は頑張ります!

皆さんも一緒に頑張りましょう!!!

以上、「え、意外とみんな知らない!?要素外でクリックしたときにイベントを発火させる方法」でした!

良かったら、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

Thank you for reading

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