- 投稿日:2019-10-12T22:47:29+09:00
Vue Head利用方法メモ
書いてあること
- Vue Headの利用方法を試した際のメモ
参考
vue-head
vue.jsでheadの要素を設定する時はvue-headが便利!環境
- MacOS 10.14.6
- Node.js v10.16.0
- Npm 6.12.0
- Vue 3.8.4
- Vue Head 2.1.2
レポジトリ
アプリケーション共通の情報
レイアウトファイルに共通情報を設定する
src/layouts/default.vue<script> export default { head: { meta() { return [{ name: 'application-name', content: 'vue-router-project' }] }, }, } </script>ページ毎の情報
各ビューファイルにページの個別情報を設定する
src/views/page.vue<script> export default { head: { title() { return { inner: 'VueRouter Project', separator: '|', complement: 'Home', } }, meta() { return [ { name: 'title', content: 'Home' }, { name: 'description', content: 'home description' } ] }, }, } </script>
- 投稿日:2019-10-12T22:26:44+09:00
Vue.js環境変数による開発作業メモ
書いてあること
- Vue.jsで環境変数を利用する方法メモ
- とりあえず1人情シスでの開発なので
.env
と.env.local
で十分かなと。参考
Modes and Environment Variables
Vue CLI 3.xからの環境変数やモードの扱い環境
- MacOS 10.14.6
- Node.js v10.16.0
- Npm 6.12.0
- Vue 3.8.4
レポジトリ
Git管理対象外に設定
.gitignore.DS_Store node_modules /dist /coverage # env files +.env .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw?環境変数を定義
.env.local
.envVUE_APP_PROJECT_NAME='VUE-PROJECT' VUE_APP_URL='https://example.com'.env.localVUE_APP_URL='https://development.example.com'環境変数を利用
src/views/page.vue<template> <v-container class="fill-height" fluid> <v-row align="center" justify="center"> <v-flex sm12> <v-card width="60%" max-width="800" elevation="18" class="mx-auto"> <v-card-title class="title">Sample Title</v-card-title> <v-divider /> <v-card-text> <v-list> <v-list-item> {{ projectName }} </v-list-item> <v-list-item> {{ url }} </v-list-item> </v-list> </v-card-text> </v-card> </v-flex> </v-row> </v-container> </template> <script> export default { data() { return { projectName: null, url: null, } }, mounted() { this.projectName = process.env.VUE_APP_PROJECT_NAME this.url = process.env.VUE_APP_URL }, } </script>動作結果
.env.local
がある場合
.env.local
がない場合
- 投稿日:2019-10-12T22:16:40+09:00
Nuxt.js+VuetifyでSASS変数の上書きが反映されない場合の確認事項
タイトル通りですが、対象読者はNuxt.jsにVuetify(@nuxtjs/vuetify)をインストール後、VuetifyのSASS変数を変更するためにvariables.scssを作成したけど反映されずに困ってる方です。
結果的には@nuxtjs/vuetify のページをじっくり読めば問題なかったのですが、要点だけ書いておきます。
1. customVariablesの設定
nuxtjs/vuetifyのcustomVariables部分を読むと、customVariablesオプションについて以下のように書かれてます。
Provide a way to customize Vuetify SASS variables.
一例として以下のようにサンプルが載ってます。
nuxt.config.jsvuetify: { customVariables: ['~/assets/variables.scss'] }※ customVariablesはNuxt.jsをインストールする際、テンプレートにVuetifyを選択すると最初から記述されてます。
2. treeShake: trueの設定
Nuxt.jsを開発モードで起動した場合、以下の内容通りcustomVariablesを設定するだけではvariables.scssの内容は反映されません。
Provide a way to customize Vuetify SASS variables.
Only works with tree-shaking.実はこのtree-shaking はリンク先の記載通りデフォルトではproductionモードでしか機能しないため、yarn run devのように開発モードで実行すると機能しません。そのため上記で設定したvariables.scssが反映されずになんで????となったのがこの記事投稿のきっかけでした。
最終的にはnuxt.config.jsにtreeShake: trueの一文を加えるだけでOKでした。
nuxt.config.jsvuetify: { customVariables: ['~/assets/variables.scss'], treeShake: true, }variables.scss// Ref: https://github.com/nuxt-community/vuetify-module#customvariables // // The variables you want to modify $font-size-root: 20px;これでassets/variables.scssを変更して保存すれば自動的にコンパイルされて変更内容が画面に反映されるようになりました。ただしコンパイル時間は若干かかります。
最後に
恐らくこの問題はNuxt.jsのインストール時のテンプレートにVuetifyを選択した人が一番ハマりやすいと思います。理由はnuxt.config.jsにcustomVariablesのオプションは最初から記載してあり、ご丁寧にassets/variables.scssも用意してるのにtreeShakeについては全く記述がないからです。
参考
- 投稿日:2019-10-12T22:16:40+09:00
Nuxt.js+VuetifyのSASS変数を上書きする方法
タイトル通りですが、対象読者はNuxt.jsにVuetify(@nuxtjs/vuetify)をインストール後、VuetifyのSASS変数を変更するためにvariables.scssを作成したけど反映されずに困ってる方です。
結果的には@nuxtjs/vuetify のページをじっくり読めば問題なかったのですが、要点だけ書いておきます。
1. customVariablesの設定
nuxtjs/vuetifyのcustomVariables部分を読むと、customVariablesオプションについて以下のように書かれてます。
Provide a way to customize Vuetify SASS variables.
一例として以下のようにサンプルが載ってます。
nuxt.config.jsvuetify: { customVariables: ['~/assets/variables.scss'] }※ customVariablesはNuxt.jsをインストールする際、テンプレートにVuetifyを選択すると最初から記述されてます。
2. treeShake: trueの設定
Nuxt.jsを開発モードで起動した場合、以下の内容通りcustomVariablesを設定するだけではvariables.scssの内容は反映されません。
Provide a way to customize Vuetify SASS variables.
Only works with tree-shaking.実はこのtree-shaking はリンク先の記載通りデフォルトではproductionモードでしか機能しないため、yarn run devのように開発モードで実行すると機能しません。そのため上記で設定したvariables.scssが反映されずになんで????となったのがこの記事投稿のきっかけでした。
最終的にはnuxt.config.jsにtreeShake: trueの一文を加えるだけでOKでした。
nuxt.config.jsvuetify: { customVariables: ['~/assets/variables.scss'], treeShake: true, }variables.scss// Ref: https://github.com/nuxt-community/vuetify-module#customvariables // // The variables you want to modify $font-size-root: 20px;これでassets/variables.scssを変更して保存すれば自動的にコンパイルされて変更内容が画面に反映されるようになりました。ただしコンパイル時間は若干かかります。
最後に
恐らくこの問題はNuxt.jsのインストール時のテンプレートにVuetifyを選択した人が一番ハマりやすいと思います。理由はnuxt.config.jsにcustomVariablesのオプションは最初から記載してあり、ご丁寧にassets/variables.scssも用意してるのにtreeShakeについては全く記述がないからです。
参考
- 投稿日:2019-10-12T21:19:15+09:00
EC2のLaravel6.0環境 VueとLaravelでのTIPS AWS/Laravel連載(10.5)
LaravelのシーディングにFakerを入れる
連載9回目で初期値でhoge, fugaという値を入れました。
実際テストする中だといちいちダミーの名前やタイトル、文章等を考えるのは一苦労です。そこで便利なのがFaker。
Laravelだと最初からcomposer.jsonに入っているので、install不要です。そしてシーディングだけでなく、Laravelのfactoryという仕組みを作って汎用性のある書き方をしてみます。
$ php artisan make:factory PostFactory --model=Postdatabase/factories/PostFactory.php<?php /** @var \Illuminate\Database\Eloquent\Factory $factory */ use App\Post; use Faker\Generator as Faker; $factory->define(Post::class, function (Faker $faker) { return [ 'title' => $faker->sentence, 'content' => $faker->paragraph, ]; });database/seeds/PostsTableSeeder.php... public function run() { $posts = factory(App\Post::class, 3)->create(); }$ php artisan db:seedこれで3件分のダミーデータが生成されます。
php artisan tinker Psy Shell v0.9.9 (PHP 7.2.22 — cli) by Justin Hileman >>> Post::all(); [!] Aliasing 'Post' to 'App\Post' for this Tinker session. => Illuminate\Database\Eloquent\Collection {#3084 all: [ App\Post {#3085 id: 1, title: "hoge", content: "fuga", created_at: null, updated_at: null, }, App\Post {#3086 id: 2, title: "Repellendus odit qui facilis ea sint.", content: "Est eos sed amet quibusdam. Et voluptatem voluptatem et accusantium qui.", created_at: "2019-10-12 21:05:10", updated_at: "2019-10-12 21:05:10", }, App\Post {#3087 id: 3, title: "Neque rerum ut molestiae aut sequi.", content: "Rem exercitationem doloremque facilis et sed qui blanditiis. Dolorem ea quo in voluptatem. Distinctio tempora dolor culpa reprehenderit fuga voluptas omnis. Animi voluptas repudiandae rem quisquam eligendi dolores. Quibusdam iste aut possimus.", created_at: "2019-10-12 21:05:10", updated_at: "2019-10-12 21:05:10", }, App\Post {#3088 id: 4, title: "Soluta est iure ex sequi a aspernatur.", content: "Eos et voluptatem sint tempore. Rerum voluptates quis est fugit voluptas sit voluptatem consequuntur. Voluptas sapiente illo quo optio.", created_at: "2019-10-12 21:05:10", updated_at: "2019-10-12 21:05:10", }, ], } >>>GitHub - fzaninotto/Faker: Faker is a PHP library that generates fake data for you
Vue開発の際は
連載10回目でVue.jsを入れました。
デバッグはChromeの開発者ツールだけだといろいろしんどいので、Vue.jsのdevtoolsを入れるのをオススメします。入れるとこんな感じで、Chromeの開発者ツールに「Vue」タブが追加され、開発が捗ります。
- 投稿日:2019-10-12T17:22:41+09:00
Vue.js用語辞典【未完】
ディレクティブ
- DOM 要素に対して何かを実行することをライブラリに伝達する、マークアップ中の特別なトークン。
- 「v-」で始まるやつ
- ディレクティブ=指令する
- Vue.jsに仕事を振る仕組み
接頭辞はデフォルトで v
- v-bind
- v-if
- v-show
- v-for
- v-on
メソッドハンドラ
- 「クリック」や「入力」といったイベントをハンドリングする
- クリックしたらアラートを出す!、マウスオーバーしたら文字が大きくなるみたいな動きをつけたい時使おう
- 簡単な「v-on」ディレクティブを使った例
See the Pen pooJzQr by アズマックス (@yuki-azuma) on CodePen.
双方向データバインディング
- 双方向にデータが同期するデータバインディング
- そもそも、データバインディングとは、「データと描画を同期する仕組み」
- Aが変わったら、Bも変わり、Bが変われば、Aも変わるイメージ
コンポーネント
- 名前付きの再利用可能な Vue インスタンス
- 必要なだけ何度でも再利用できる
部品ごとに使われることが多い
- 例)
- 画面のヘッダー部分のコンポーネント
- フッター部分のコンポーネント、
- サイドメニューのコンポーネント
開発規模が大きくなってきたら、コンポーネントは大活躍する
以下のワードもよく出てくるので、覚えておきたい
- $emit
- props
- ローカルコンポーネント
- グローバルコンポーネント
その他参考
- 投稿日:2019-10-12T16:30:44+09:00
vueで定数定義をいい感じにするtips
- 投稿日:2019-10-12T16:05:00+09:00
Nuxt.js + Expressでローカルサーバ構築
今回のゴール
- ローカルに擬似的なサーバ(スタブ)を用意して特定のリクエストで(以下イメージではPOST)レスポンスが返ってくるようにします。
ローカルサーバを使いたい局面
- フロントとバックエンドでチームが別れて開発されているような現場でバックエンド側の実装ができていないときにフロント側からリクエストを飛ばしてデータが返却されるようなスタブが欲しい!!そんなとき。
expressの導入
既存のアプリケーションにexpressを導入したい場合
npm install express --saveサーバ用のファイルをプロジェクトフォルダ直下へ作成
- まずは必要なフォルダ・ファイルの作成をします。
- プロジェクトフォルダ直下へapiフォルダを作ってその中に
server.js
を作成、続いてmock
フォルダをserver.jsと同じ階層に作成しprofile
フォルダを作ります(profileフォルダ内に実際にレスポンスとして返すjsonを格納します)//プロジェクトフォルダ直下にて $ mkdir api $ cd api $ touch server.js $ mkdir mocks $ cd mocks $ mkdir profile $ cd profile $ touch test.json完成した階層
プロジェクトフォルダ ┗api ┗server.js//サーバの設定を書くファイル ┗mocks ┗profile ┗test.json//実際に返して欲しいデータを定義するファイル
- ちょっとわかりにくいので画像も
server.jsを編集
- 以下のように記述
server.jsconst express = require('express') const app = express() app.post('/test', (req, res) => res.json(require('./mocks/profile/test.json'))) //あとは欲しいだけ上記の1行とjsonファイルを追加するだけ app.listen(8080, () => console.log('API Mock Server is running'))
- expressをappとして用意し、
8080
ポートをlistenしています。app.post(リクエスト,(req, res) => res.(require(返却して欲しいデータを記述したjson)))
とすることで最初のゴールのようなレスポンスが返却されます。- あとは欲しいだけ上記の1行とjsonファイルを追加するだけ
jsonサンプル
- 内容はなんでもいいのですがゴールの動画で食わしたjsonは以下です。
test.json[ { "header": "テスト テスト", "test1": "test", "test2": "テスと", "test3": "1", "items": [] } ]package.jsonに起動コマンドを追加する
package.json"scripts": { //省略 "mock:api": "node api/server.js"//追加 }
- これで
npm run mock:api
でローカルサーバが起動できます。起動とcurlでPOST
- 起動
npm run mock:api
- POST(別のターミナルを開いて)
curl -XPOST http://localhost:8080/test
- これでゴールのようなレスポンスが返ってきているはずです
所感
- nuxt.jsのフロントに特化した開発担当になった場合は使えるかな、と。以上です。
- 投稿日:2019-10-12T14:16:15+09:00
はてなブックマークのコメントを Qiita 記事内に埋め込む Chrome 拡張機能 を Vue.js で作った
はじめに
Qiita もはてなもこよなく愛する方のために、以下画像のように Qiita の記事の終わりにはてなブックマークのコメントを読み込んで表示する拡張機能 Hatiina を作った。
※拡張機能を公開したのが 2019/10/12 なので、もしかしたら審査リジェクトで見えなくなる期間が出てくるかも。
TL:DR;
- クエリパラメータ(
?hoge=fuga&foo=bar
)やアンカー(#xxx
)付きの URL でもブコメを表示できる
- メールなどから Qiita にアクセスした場合とか
- スター獲得数順にソート
- 実装には Vue.js (とその他いくつかの NPM packages)を利用
作ろうと思った動機
1. 普段の作業を簡略化するため
自分はググって Qiita を見ることが多いが、毎週来る Qiita のニュースメールや Twitter からの通知メールから気になった記事を見ることも結構ある。こういうやつ。
で、記事本文だけでなく、なるべく記事中のコメントやはてなブックマークのコメントも見ることで、「記事中で言及されている内容に対して、他の人や玄人はどう考えているのか」や「他に良いツール/やり方/考え方は無いか」などの参考にしている。のだが、↑のリンクから飛ぶとはてなブックマーク拡張機能でコメントが読み込めない。
画像から分かるかもしれないが、原因は URL 内のクエリパラメータやアンカーにある。記事本文の素の URL にページ移動するとコメントが正しく表示できる。
…できるんだけど、「メールから記事開く」→「アドレスバーにフォーカス」→「URL 修正して移動」→「はてなブックマーク拡張機能をクリック」という手順が無駄だし面倒すぎてイライラしていた。
というわけで作った。
2. 技術的に試したいことがあった
Vue.js を使った拡張機能の記事をいくつか見たが、どれもメインページ側(という表現で良いか不安)に作用するものではなく、ポップアップページ(拡張機能アイコンのクリックで表示されるやつ)に関するものだった。(自分が見た範囲では)
- Vue.jsを使ったブラウザ拡張機能の作り方 - 物置小屋
- Chrome拡張機能開発でVue.jsを動かす – myMemoBlog by 256hax
- Vue.jsを使ってTwilio管理コンソールのアカウントを切り替えるChrome拡張機能作った | | 針は飲まれぬ
「俺が Vue.js を使いたいのはそこじゃないんだよ!」と思って「無いなら試してしまえ」の精神で作った。ちなみに「使ってみたかった」なので、「Vue.js じゃないと作れない」というものではない。
拡張機能の紹介
ウェブストアの画像や説明が雑なのはご愛嬌ということで…
できること
ページ遷移なしに「素の URL」に付いているブコメを読み込んで表示。
もうほぼ説明することは無い(前章までに書いてある)ので、簡単にスクショで紹介。
読み込み直後
ブコメは閉じた状態、クリックすると展開される
ブコメを展開
スター数順にソートされた状態。ブックマーク追加ページへのリンクや、各ユーザーへのリンクなども表示。
ブコメが無い場合
こうなる。
できないこと
あくまで「読み込んで表示」だけなので、ページ内でブックマークしたり、コメントにスターを付けたりはできない。ただし、リンク付けてるので(面倒だけど)ページ遷移すればできるはず。
技術的な話
(初歩的な内容も含むが)拡張機能内で使っているツールや設定などを書いていく。「もっと他にいいやり方あるよ」とかあればコメント貰えると嬉しい。
リポジトリ: 17number/chrome-extension-hatiina: Chrome extension for qiita
開発環境
macBook Pro で開発した。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15 BuildVersion: 19A583 $ node -v v12.7.0 $ npm -v 6.10.3使用パッケージ
package.json
よりpackage.json"dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/free-brands-svg-icons": "^5.11.2", "@fortawesome/free-regular-svg-icons": "^5.11.2", "@fortawesome/free-solid-svg-icons": "^5.11.2", "@fortawesome/vue-fontawesome": "^0.1.7", "axios": "^0.19.0", "crx-hotreload": "^1.0.4", "moment-timezone": "^0.5.26", "vue": "^2.6.10", "webextension-polyfill": "^0.5.0" }, "devDependencies": { "@vue/cli-service": "^3.11.0", "cpx": "^1.5.0", "mkdirp2": "^1.0.4", "node-sass": "^4.12.0", "npm-run-all": "^4.1.5", "npm-watch": "^0.6.0", "rimraf": "^3.0.0", "sass-loader": "^8.0.0", "terser-webpack-plugin": "^2.1.3", "vue-svg-loader": "^0.12.0", "vue-template-compiler": "^2.6.10" }Vue.js を主ページ側で使う方法
整理すればそんなに難しいことじゃない。
(ここに辿り着くまでに割と右往左往した)
manifest.json
で先に Vue.js (の定義されたファイル)を読み込む- Vue.js のターゲットとなる HTML 要素を埋め込む
- 埋め込まれた HTML 要素に対して Vue のインスタンスを生成する
manifest.json{ ... "content_scripts": [ { "matches": [ "https://qiita.com/*/items*" ], "js": [ "js/chunk-vendors.js", # Vue.js のコードはここに入っている(ので先に読み込む) "js/app.js" # ここで Vue.js のインスタンス生成とか ], } ], ... }
app.js
(エントリポイント)app.jsimport Vue from "vue"; import App from "./App.vue"; (() => { 'use strict'; document .querySelector(".p-items > .p-items_container > .p-items_main") .insertAdjacentHTML( `afterBegin`, `<div id="hatiina"></div>` ); new Vue({ render: h => h(App), }).$mount("#hatiina"); })();この辺りの話は自分のブログに書いているので合わせて載せておく。
Chrome 拡張機能の開発中にホットリロード(自動読み込み)する方法
ビルド関連
ビルドは
vue-cli-service
で行う。Content Scripts 側と Background Scripts 側とでコマンドを分けている。Content Scripts 側
--no-clean
オプションを付けている理由は後述。package.json"scripts": { "compile:content": "vue-cli-service build --no-clean src/app.js", },ポイントは
vue.config.js
による設定で、filenameHashing
とproductionSourceMap
をfalse
にしていること。vue.config.jsmodule.exports = { filenameHashing: false, productionSourceMap: false, };
filenameHashing
の設定をしていないと、app.1234abcd.js
のようなファイル名で出力されてしまい、manifest.json
で指定するソースファイルの記述を修正しないといけなくなる。頑張れば自動で変更できると思うが、それよりはファイル名を固定にした方が良い。
productionSourceMap
の設定は、ソースマップファイルは不要だよね、というだけ。Background Scripts 側
Background Scripts 側のビルドは色々と試した結果、以下の設定だと上手く動作した。
package.json"scripts": { "compile:background": "vue-cli-service build --target lib --formats umd --dest dist/js --no-clean --name background src/js/background.js", },これで
src/js/background.js
(とそこから読み込まれているパッケージなど)が、dist/js/background.umd.js
に出力される。「なぜそのオプションにしたのか」については、何となくは分かるが明確に言語化できない(= 理解が浅い)ので、誤解がうまれるのを避けるためにも言及しない。
※よくよく考えると、別に Vue.js 関係ない(はてなブックマークの API を叩いて返すだけ)から、別にvue-cli-service
使う必要はなかったんじゃ…
--no-clean
オプションの理由以下の記事を参考に、ファイル変更を検知してホットリロードさせている。
Chrome Extensionの開発時にホットリロードさせる - Qiita
(試してないので確実ではないが)
--no-clean
を付けずにビルドして、dist
ディレクトリごと削除されると挙動がおかしくなるのでは?と思い、既存コードは残したまま上書きするようにした。自分のブログで NPM 使う設定例も書いているので、ついでに載s
Chrome 拡張機能の開発中にホットリロード(自動読み込み)する方法
その他
FontAwesome
FontAwesome は Vue.js Project で使用可能な公式パッケージがあるのでそれを利用。
Font Awesome now has an official Vue.js component that’s available for all who want to use our icons in their Vue.js projects.
Vue.js | Font Awesome より@fortawesome/vue-fontawesome の Usage に書かれている通り、
import
して Vue のインスタンス生成前にゴニョゴニョすれば、<font-awesome-icon />
というタグで表示できる。app.jsimport { library } from '@fortawesome/fontawesome-svg-core'; // ↓は使いたいアイコン // 今回は bookmark(https://fontawesome.com/icons/bookmark?style=solid) なので faBookmark import { faBookmark } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; (() => { ... // FontAwesome Setup library.add(faBookmark) Vue.component('font-awesome-icon', FontAwesomeIcon) Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount("#hatiina"); })();
:icon
で Style(Regular(far
) とか Solid(fas
) とか) とアイコン名を指定する。xxx.vue<template> <div id="app"> <font-awesome-icon :icon="['fas', 'bookmark']" /> </div> </template>以下の記事が分かりやすい。
Font awesome を Vue.js で使ってみよう - Qiita
画像・アイコン
はてなスター
はてなスターの画像は安定のいらすとやから拝借して利用。
いろいろな色の星のイラスト | かわいいフリー素材集 いらすとや
上記をダウンロードして、画像サイズを小さくした後に、TinyPNG で圧縮。
はてなブックマーク
公式素材があるのでそれを利用。SVG だったので、そのままプロジェクト内に取り込み。
拡張機能アイコン・ウェブストア画像
Canva で作成。
フォントはうずらフォントを使用
、理由はなんとなく気に入ったから。ウェブストアの説明や画像はそのうちどうにかする予定…
Hatiina のイケてない点
API Request 数が多い
はてなスター数を取ってくるのに API を連打してしまっている。これは解決手段が分かってるので、別途修正予定。
なんかスター数が多い
公式拡張機能のカウント数と見比べると分かるんだけど、なんか表示されるスター数に差がある。
とは言え、全体的に多くカウントされる(= みんな多い)だけなので、(個人的には)大した問題じゃないと思って放置予定。
ブコメ数が多いとダルい
ブコメを閉じるには、一番上の箇所をクリックするしかないんだけど、全200コメント中の100コメントあたりを見ている状態だと、閉じるにはスクロールしまくるしかない。
ウィンドウ幅を狭くした時の見た目
めっちゃ狭くするとユーザーアイコンの表示が変になったり、特定の幅で Qiita 本体のレイアウトと適合しない配置になったりする。
本質的な問題じゃないので放置。
共通化などが中途半端(な気がする)
もう少しコンポーネント分割できそうな気がする。
あと、Vue.js の(把握できていない)便利機能とかで、もっとシンプルに書けそうな気もする。
あとがき
メールとかから飛んでもブコメ見れるから、はてブ民は良ければ使ってくれよな。
※拡張機能を公開したのが 2019/10/12 なので、もしかしたら審査リジェクトで見えなくなる期間が出てくるかも。
リポジトリも公開してるから「変な拡張機能怖い」って人とか、「おかしな実装ないか調べたろ!」って人も安心だよ。
17number/chrome-extension-hatiina: Chrome extension for qiita
- 投稿日:2019-10-12T14:16:15+09:00
はてなブックマークのコメントを Qiita 記事内に表示する Chrome 拡張機能 を Vue.js で作った話
はじめに
Qiita もはてなもこよなく愛する方のために、以下画像のように Qiita の記事の終わりにはてなブックマークのコメントを読み込んで表示する拡張機能 Hatiina を作った。
※拡張機能を公開したのが 2019/10/12 なので、もしかしたら審査リジェクトで見えなくなる期間が出てくるかも。
TL:DR;
- クエリパラメータ(
?hoge=fuga&foo=bar
)やアンカー(#xxx
)付きの URL でもブコメを表示できる
- メールなどから Qiita にアクセスした場合とか
- スター獲得数順にソート
- 実装には Vue.js (とその他いくつかの NPM packages)を利用
作ろうと思った動機
1. 普段の作業を簡略化するため
自分はググって Qiita を見ることが多いが、毎週来る Qiita のニュースメールや Twitter からの通知メールから気になった記事を見ることも結構ある。こういうやつ。
で、記事本文だけでなく、なるべく記事中のコメントやはてなブックマークのコメントも見ることで、「記事中で言及されている内容に対して、他の人や玄人はどう考えているのか」や「他に良いツール/やり方/考え方は無いか」などの参考にしている。のだが、↑のリンクから飛ぶとはてなブックマーク拡張機能でコメントが読み込めない。
画像から分かるかもしれないが、原因は URL 内のクエリパラメータやアンカーにある。記事本文の素の URL にページ移動するとコメントが正しく表示できる。
…できるんだけど、「メールから記事開く」→「アドレスバーにフォーカス」→「URL 修正して移動」→「はてなブックマーク拡張機能をクリック」という手順が無駄だし面倒すぎてイライラしていた。
というわけで作った。
2. 技術的に試したいことがあった
Vue.js を使った拡張機能の記事をいくつか見たが、どれもメインページ側(という表現で良いか不安)に作用するものではなく、ポップアップページ(拡張機能アイコンのクリックで表示されるやつ)に関するものだった。(自分が見た範囲では)
- Vue.jsを使ったブラウザ拡張機能の作り方 - 物置小屋
- Chrome拡張機能開発でVue.jsを動かす – myMemoBlog by 256hax
- Vue.jsを使ってTwilio管理コンソールのアカウントを切り替えるChrome拡張機能作った | | 針は飲まれぬ
「俺が Vue.js を使いたいのはそこじゃないんだよ!」と思って「無いなら試してしまえ」の精神で作った。ちなみに「使ってみたかった」なので、「Vue.js じゃないと作れない」というものではない。
拡張機能の紹介
ウェブストアの画像や説明が雑なのはご愛嬌ということで…
できること
ページ遷移なしに「素の URL」に付いているブコメを読み込んで表示。
もうほぼ説明することは無い(前章までに書いてある)ので、簡単にスクショで紹介。
読み込み直後
ブコメは閉じた状態、クリックすると展開される
ブコメを展開
スター数順にソートされた状態。ブックマーク追加ページへのリンクや、各ユーザーへのリンクなども表示。
ブコメが無い場合
こうなる。
できないこと
あくまで「読み込んで表示」だけなので、ページ内でブックマークしたり、コメントにスターを付けたりはできない。ただし、リンク付けてるので(面倒だけど)ページ遷移すればできるはず。
技術的な話
(初歩的な内容も含むが)拡張機能内で使っているツールや設定などを書いていく。「もっと他にいいやり方あるよ」とかあればコメント貰えると嬉しい。
リポジトリ: 17number/chrome-extension-hatiina: Chrome extension for qiita
開発環境
macBook Pro で開発した。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15 BuildVersion: 19A583 $ node -v v12.7.0 $ npm -v 6.10.3使用パッケージ
package.json
よりpackage.json"dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/free-brands-svg-icons": "^5.11.2", "@fortawesome/free-regular-svg-icons": "^5.11.2", "@fortawesome/free-solid-svg-icons": "^5.11.2", "@fortawesome/vue-fontawesome": "^0.1.7", "axios": "^0.19.0", "crx-hotreload": "^1.0.4", "moment-timezone": "^0.5.26", "vue": "^2.6.10", "webextension-polyfill": "^0.5.0" }, "devDependencies": { "@vue/cli-service": "^3.11.0", "cpx": "^1.5.0", "mkdirp2": "^1.0.4", "node-sass": "^4.12.0", "npm-run-all": "^4.1.5", "npm-watch": "^0.6.0", "rimraf": "^3.0.0", "sass-loader": "^8.0.0", "terser-webpack-plugin": "^2.1.3", "vue-svg-loader": "^0.12.0", "vue-template-compiler": "^2.6.10" }Vue.js を主ページ側で使う方法
整理すればそんなに難しいことじゃない。
(ここに辿り着くまでに割と右往左往した)
manifest.json
で先に Vue.js (の定義されたファイル)を読み込む- Vue.js のターゲットとなる HTML 要素を埋め込む
- 埋め込まれた HTML 要素に対して Vue のインスタンスを生成する
manifest.json{ ... "content_scripts": [ { "matches": [ "https://qiita.com/*/items*" ], "js": [ "js/chunk-vendors.js", # Vue.js のコードはここに入っている(ので先に読み込む) "js/app.js" # ここで Vue.js のインスタンス生成とか ], } ], ... }
app.js
(エントリポイント)app.jsimport Vue from "vue"; import App from "./App.vue"; (() => { 'use strict'; document .querySelector(".p-items > .p-items_container > .p-items_main") .insertAdjacentHTML( `afterBegin`, `<div id="hatiina"></div>` ); new Vue({ render: h => h(App), }).$mount("#hatiina"); })();この辺りの話は自分のブログに書いているので合わせて載せておく。
Chrome 拡張機能の開発中にホットリロード(自動読み込み)する方法
ビルド関連
ビルドは
vue-cli-service
で行う。Content Scripts 側と Background Scripts 側とでコマンドを分けている。Content Scripts 側
--no-clean
オプションを付けている理由は後述。package.json"scripts": { "compile:content": "vue-cli-service build --no-clean src/app.js", },ポイントは
vue.config.js
による設定で、filenameHashing
とproductionSourceMap
をfalse
にしていること。vue.config.jsmodule.exports = { filenameHashing: false, productionSourceMap: false, };
filenameHashing
の設定をしていないと、app.1234abcd.js
のようなファイル名で出力されてしまい、manifest.json
で指定するソースファイルの記述を修正しないといけなくなる。頑張れば自動で変更できると思うが、それよりはファイル名を固定にした方が良い。
productionSourceMap
の設定は、ソースマップファイルは不要だよね、というだけ。Background Scripts 側
Background Scripts 側のビルドは色々と試した結果、以下の設定だと上手く動作した。
package.json"scripts": { "compile:background": "vue-cli-service build --target lib --formats umd --dest dist/js --no-clean --name background src/js/background.js", },これで
src/js/background.js
(とそこから読み込まれているパッケージなど)が、dist/js/background.umd.js
に出力される。「なぜそのオプションにしたのか」については、何となくは分かるが明確に言語化できない(= 理解が浅い)ので、誤解がうまれるのを避けるためにも言及しない。
※よくよく考えると、別に Vue.js 関係ない(はてなブックマークの API を叩いて返すだけ)から、別にvue-cli-service
使う必要はなかったんじゃ…
--no-clean
オプションの理由以下の記事を参考に、ファイル変更を検知してホットリロードさせている。
Chrome Extensionの開発時にホットリロードさせる - Qiita
(試してないので確実ではないが)
--no-clean
を付けずにビルドして、dist
ディレクトリごと削除されると挙動がおかしくなるのでは?と思い、既存コードは残したまま上書きするようにした。自分のブログで NPM 使う設定例も書いているので、ついでに載s
Chrome 拡張機能の開発中にホットリロード(自動読み込み)する方法
その他
FontAwesome
FontAwesome は Vue.js Project で使用可能な公式パッケージがあるのでそれを利用。
Font Awesome now has an official Vue.js component that’s available for all who want to use our icons in their Vue.js projects.
Vue.js | Font Awesome より@fortawesome/vue-fontawesome の Usage に書かれている通り、
import
して Vue のインスタンス生成前にゴニョゴニョすれば、<font-awesome-icon />
というタグで表示できる。app.jsimport { library } from '@fortawesome/fontawesome-svg-core'; // ↓は使いたいアイコン // 今回は bookmark(https://fontawesome.com/icons/bookmark?style=solid) なので faBookmark import { faBookmark } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; (() => { ... // FontAwesome Setup library.add(faBookmark) Vue.component('font-awesome-icon', FontAwesomeIcon) Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount("#hatiina"); })();
:icon
で Style(Regular(far
) とか Solid(fas
) とか) とアイコン名を指定する。xxx.vue<template> <div id="app"> <font-awesome-icon :icon="['fas', 'bookmark']" /> </div> </template>以下の記事が分かりやすい。
Font awesome を Vue.js で使ってみよう - Qiita
画像・アイコン
はてなスター
はてなスターの画像は安定のいらすとやから拝借して利用。
いろいろな色の星のイラスト | かわいいフリー素材集 いらすとや
上記をダウンロードして、画像サイズを小さくした後に、TinyPNG で圧縮。
はてなブックマーク
公式素材があるのでそれを利用。SVG だったので、そのままプロジェクト内に取り込み。
拡張機能アイコン・ウェブストア画像
Canva で作成。
フォントはうずらフォントを使用
、理由はなんとなく気に入ったから。ウェブストアの説明や画像はそのうちどうにかする予定…
Hatiina のイケてない点
API Request 数が多い
はてなスター数を取ってくるのに API を連打してしまっている。これは解決手段が分かってるので、別途修正予定。
なんかスター数が多い
公式拡張機能のカウント数と見比べると分かるんだけど、なんか表示されるスター数に差がある。
とは言え、全体的に多くカウントされる(= みんな多い)だけなので、(個人的には)大した問題じゃないと思って放置予定。
ブコメ数が多いとダルい
ブコメを閉じるには、一番上の箇所をクリックするしかないんだけど、全200コメント中の100コメントあたりを見ている状態だと、閉じるにはスクロールしまくるしかない。
ウィンドウ幅を狭くした時の見た目
めっちゃ狭くするとユーザーアイコンの表示が変になったり、特定の幅で Qiita 本体のレイアウトと適合しない配置になったりする。
本質的な問題じゃないので放置。
共通化などが中途半端(な気がする)
もう少しコンポーネント分割できそうな気がする。
あと、Vue.js の(把握できていない)便利機能とかで、もっとシンプルに書けそうな気もする。
あとがき
メールとかから飛んでもブコメ見れるから、はてブ民は良ければ使ってくれよな。
※拡張機能を公開したのが 2019/10/12 なので、もしかしたら審査リジェクトで見えなくなる期間が出てくるかも。
リポジトリも公開してるから「変な拡張機能怖い」って人とか、「おかしな実装ないか調べたろ!」って人も安心だよ。
17number/chrome-extension-hatiina: Chrome extension for qiita
- 投稿日:2019-10-12T14:16:15+09:00
Vue.js を使ってChrome 拡張機能を作った話(はてなブックマークのコメントを Qiita 記事内に表示)
はじめに
Qiita もはてなもこよなく愛する方のために、以下画像のように Qiita の記事の終わりにはてなブックマークのコメントを読み込んで表示する拡張機能 Hatiina を作った。
※拡張機能を公開したのが 2019/10/12 なので、もしかしたら審査リジェクトで見えなくなる期間が出てくるかも。
TL:DR;
- クエリパラメータ(
?hoge=fuga&foo=bar
)やアンカー(#xxx
)付きの URL でもブコメを表示できる
- メールなどから Qiita にアクセスした場合とか
- スター獲得数順にソート
- 実装には Vue.js (とその他いくつかの NPM packages)を利用
作ろうと思った動機
1. 普段の作業を簡略化するため
自分はググって Qiita を見ることが多いが、毎週来る Qiita のニュースメールや Twitter からの通知メールから気になった記事を見ることも結構ある。こういうやつ。
で、記事本文だけでなく、なるべく記事中のコメントやはてなブックマークのコメントも見ることで、「記事中で言及されている内容に対して、他の人や玄人はどう考えているのか」や「他に良いツール/やり方/考え方は無いか」などの参考にしている。のだが、↑のリンクから飛ぶとはてなブックマーク拡張機能でコメントが読み込めない。
画像から分かるかもしれないが、原因は URL 内のクエリパラメータやアンカーにある。記事本文の素の URL にページ移動するとコメントが正しく表示できる。
…できるんだけど、「メールから記事開く」→「アドレスバーにフォーカス」→「URL 修正して移動」→「はてなブックマーク拡張機能をクリック」という手順が無駄だし面倒すぎてイライラしていた。
というわけで作った。
2. 技術的に試したいことがあった
Vue.js を使った拡張機能の記事をいくつか見たが、どれもメインページ側(という表現で良いか不安)に作用するものではなく、ポップアップページ(拡張機能アイコンのクリックで表示されるやつ)に関するものだった。(自分が見た範囲では)
- Vue.jsを使ったブラウザ拡張機能の作り方 - 物置小屋
- Chrome拡張機能開発でVue.jsを動かす – myMemoBlog by 256hax
- Vue.jsを使ってTwilio管理コンソールのアカウントを切り替えるChrome拡張機能作った | | 針は飲まれぬ
「俺が Vue.js を使いたいのはそこじゃないんだよ!」と思って「無いなら試してしまえ」の精神で作った。ちなみに「使ってみたかった」なので、「Vue.js じゃないと作れない」というものではない。
拡張機能の紹介
ウェブストアの画像や説明が雑なのはご愛嬌ということで…
できること
ページ遷移なしに「素の URL」に付いているブコメを読み込んで表示。
もうほぼ説明することは無い(前章までに書いてある)ので、簡単にスクショで紹介。
読み込み直後
ブコメは閉じた状態、クリックすると展開される
ブコメを展開
スター数順にソートされた状態。ブックマーク追加ページへのリンクや、各ユーザーへのリンクなども表示。
ブコメが無い場合
こうなる。
できないこと
あくまで「読み込んで表示」だけなので、ページ内でブックマークしたり、コメントにスターを付けたりはできない。ただし、リンク付けてるので(面倒だけど)ページ遷移すればできるはず。
技術的な話
(初歩的な内容も含むが)拡張機能内で使っているツールや設定などを書いていく。「もっと他にいいやり方あるよ」とかあればコメント貰えると嬉しい。
リポジトリ: 17number/chrome-extension-hatiina: Chrome extension for qiita
開発環境
macBook Pro で開発した。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15 BuildVersion: 19A583 $ node -v v12.7.0 $ npm -v 6.10.3使用パッケージ
package.json
よりpackage.json"dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/free-brands-svg-icons": "^5.11.2", "@fortawesome/free-regular-svg-icons": "^5.11.2", "@fortawesome/free-solid-svg-icons": "^5.11.2", "@fortawesome/vue-fontawesome": "^0.1.7", "axios": "^0.19.0", "crx-hotreload": "^1.0.4", "moment-timezone": "^0.5.26", "vue": "^2.6.10", "webextension-polyfill": "^0.5.0" }, "devDependencies": { "@vue/cli-service": "^3.11.0", "cpx": "^1.5.0", "mkdirp2": "^1.0.4", "node-sass": "^4.12.0", "npm-run-all": "^4.1.5", "npm-watch": "^0.6.0", "rimraf": "^3.0.0", "sass-loader": "^8.0.0", "terser-webpack-plugin": "^2.1.3", "vue-svg-loader": "^0.12.0", "vue-template-compiler": "^2.6.10" }Vue.js を主ページ側で使う方法
整理すればそんなに難しいことじゃない。
(ここに辿り着くまでに割と右往左往した)
manifest.json
で先に Vue.js (の定義されたファイル)を読み込む- Vue.js のターゲットとなる HTML 要素を埋め込む
- 埋め込まれた HTML 要素に対して Vue のインスタンスを生成する
manifest.json{ ... "content_scripts": [ { "matches": [ "https://qiita.com/*/items*" ], "js": [ "js/chunk-vendors.js", # Vue.js のコードはここに入っている(ので先に読み込む) "js/app.js" # ここで Vue.js のインスタンス生成とか ], } ], ... }
app.js
(エントリポイント)app.jsimport Vue from "vue"; import App from "./App.vue"; (() => { 'use strict'; document .querySelector(".p-items > .p-items_container > .p-items_main") .insertAdjacentHTML( `afterBegin`, `<div id="hatiina"></div>` ); new Vue({ render: h => h(App), }).$mount("#hatiina"); })();この辺りの話は自分のブログに書いているので合わせて載せておく。
Chrome 拡張機能の開発中にホットリロード(自動読み込み)する方法
ビルド関連
ビルドは
vue-cli-service
で行う。Content Scripts 側と Background Scripts 側とでコマンドを分けている。Content Scripts 側
--no-clean
オプションを付けている理由は後述。package.json"scripts": { "compile:content": "vue-cli-service build --no-clean src/app.js", },ポイントは
vue.config.js
による設定で、filenameHashing
とproductionSourceMap
をfalse
にしていること。vue.config.jsmodule.exports = { filenameHashing: false, productionSourceMap: false, };
filenameHashing
の設定をしていないと、app.1234abcd.js
のようなファイル名で出力されてしまい、manifest.json
で指定するソースファイルの記述を修正しないといけなくなる。頑張れば自動で変更できると思うが、それよりはファイル名を固定にした方が良い。
productionSourceMap
の設定は、ソースマップファイルは不要だよね、というだけ。Background Scripts 側
Background Scripts 側のビルドは色々と試した結果、以下の設定だと上手く動作した。
package.json"scripts": { "compile:background": "vue-cli-service build --target lib --formats umd --dest dist/js --no-clean --name background src/js/background.js", },これで
src/js/background.js
(とそこから読み込まれているパッケージなど)が、dist/js/background.umd.js
に出力される。「なぜそのオプションにしたのか」については、何となくは分かるが明確に言語化できない(= 理解が浅い)ので、誤解がうまれるのを避けるためにも言及しない。
※よくよく考えると、別に Vue.js 関係ない(はてなブックマークの API を叩いて返すだけ)から、別にvue-cli-service
使う必要はなかったんじゃ…
--no-clean
オプションの理由以下の記事を参考に、ファイル変更を検知してホットリロードさせている。
Chrome Extensionの開発時にホットリロードさせる - Qiita
(試してないので確実ではないが)
--no-clean
を付けずにビルドして、dist
ディレクトリごと削除されると挙動がおかしくなるのでは?と思い、既存コードは残したまま上書きするようにした。自分のブログで NPM 使う設定例も書いているので、ついでに載s
Chrome 拡張機能の開発中にホットリロード(自動読み込み)する方法
その他
FontAwesome
FontAwesome は Vue.js Project で使用可能な公式パッケージがあるのでそれを利用。
Font Awesome now has an official Vue.js component that’s available for all who want to use our icons in their Vue.js projects.
Vue.js | Font Awesome より@fortawesome/vue-fontawesome の Usage に書かれている通り、
import
して Vue のインスタンス生成前にゴニョゴニョすれば、<font-awesome-icon />
というタグで表示できる。app.jsimport { library } from '@fortawesome/fontawesome-svg-core'; // ↓は使いたいアイコン // 今回は bookmark(https://fontawesome.com/icons/bookmark?style=solid) なので faBookmark import { faBookmark } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; (() => { ... // FontAwesome Setup library.add(faBookmark) Vue.component('font-awesome-icon', FontAwesomeIcon) Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount("#hatiina"); })();
:icon
で Style(Regular(far
) とか Solid(fas
) とか) とアイコン名を指定する。xxx.vue<template> <div id="app"> <font-awesome-icon :icon="['fas', 'bookmark']" /> </div> </template>以下の記事が分かりやすい。
Font awesome を Vue.js で使ってみよう - Qiita
画像・アイコン
はてなスター
はてなスターの画像は安定のいらすとやから拝借して利用。
いろいろな色の星のイラスト | かわいいフリー素材集 いらすとや
上記をダウンロードして、画像サイズを小さくした後に、TinyPNG で圧縮。
はてなブックマーク
公式素材があるのでそれを利用。SVG だったので、そのままプロジェクト内に取り込み。
拡張機能アイコン・ウェブストア画像
Canva で作成。
フォントはうずらフォントを使用
、理由はなんとなく気に入ったから。ウェブストアの説明や画像はそのうちどうにかする予定…
Hatiina のイケてない点
API Request 数が多い
はてなスター数を取ってくるのに API を連打してしまっている。これは解決手段が分かってるので、別途修正予定。
なんかスター数が多い
公式拡張機能のカウント数と見比べると分かるんだけど、なんか表示されるスター数に差がある。
とは言え、全体的に多くカウントされる(= みんな多い)だけなので、(個人的には)大した問題じゃないと思って放置予定。
ブコメ数が多いとダルい
ブコメを閉じるには、一番上の箇所をクリックするしかないんだけど、全200コメント中の100コメントあたりを見ている状態だと、閉じるにはスクロールしまくるしかない。
ウィンドウ幅を狭くした時の見た目
めっちゃ狭くするとユーザーアイコンの表示が変になったり、特定の幅で Qiita 本体のレイアウトと適合しない配置になったりする。
本質的な問題じゃないので放置。
共通化などが中途半端(な気がする)
もう少しコンポーネント分割できそうな気がする。
あと、Vue.js の(把握できていない)便利機能とかで、もっとシンプルに書けそうな気もする。
あとがき
メールとかから飛んでもブコメ見れるから、はてブ民は良ければ使ってくれよな。
※拡張機能を公開したのが 2019/10/12 なので、もしかしたら審査リジェクトで見えなくなる期間が出てくるかも。
リポジトリも公開してるから「変な拡張機能怖い」って人とか、「おかしな実装ないか調べたろ!」って人も安心だよ。
17number/chrome-extension-hatiina: Chrome extension for qiita
- 投稿日:2019-10-12T14:16:15+09:00
はてなブックマークのコメントを Qiita 記事内に埋め込む Chrome 拡張機能 Hatiina を Vue.js で作った
はじめに
Qiita もはてなもこよなく愛する方のために、以下画像のように Qiita の記事の終わりにはてなブックマークのコメントを読み込んで表示する拡張機能 Hatiina を作った。
※拡張機能を公開したのが 2019/10/12 なので、もしかしたら審査リジェクトで見えなくなる期間が出てくるかも。
TL:DR;
- クエリパラメータ(
?hoge=fuga&foo=bar
)やアンカー(#xxx
)付きの URL でもブコメを表示できる
- メールなどから Qiita にアクセスした場合とか
- スター獲得数順にソート
- 実装には Vue.js (とその他いくつかの NPM packages)を利用
作ろうと思った動機
1. 普段の作業を簡略化するため
自分はググって Qiita を見ることが多いが、毎週来る Qiita のニュースメールや Twitter からの通知メールから気になった記事を見ることも結構ある。こういうやつ。
で、記事本文だけでなく、なるべく記事中のコメントやはてなブックマークのコメントも見ることで、「記事中で言及されている内容に対して、他の人や玄人はどう考えているのか」や「他に良いツール/やり方/考え方は無いか」などの参考にしている。のだが、↑のリンクから飛ぶとはてなブックマーク拡張機能でコメントが読み込めない。
画像から分かるかもしれないが、原因は URL 内のクエリパラメータやアンカーにある。記事本文の素の URL にページ移動するとコメントが正しく表示できる。
…できるんだけど、「メールから記事開く」→「アドレスバーにフォーカス」→「URL 修正して移動」→「はてなブックマーク拡張機能をクリック」という手順が無駄だし面倒すぎてイライラしていた。
というわけで作った。
2. 技術的に試したいことがあった
Vue.js を使った拡張機能の記事をいくつか見たが、どれもメインページ側(という表現で良いか不安)に作用するものではなく、ポップアップページ(拡張機能アイコンのクリックで表示されるやつ)に関するものだった。(自分が見た範囲では)
- Vue.jsを使ったブラウザ拡張機能の作り方 - 物置小屋
- Chrome拡張機能開発でVue.jsを動かす – myMemoBlog by 256hax
- Vue.jsを使ってTwilio管理コンソールのアカウントを切り替えるChrome拡張機能作った | | 針は飲まれぬ
「俺が Vue.js を使いたいのはそこじゃないんだよ!」と思って「無いなら試してしまえ」の精神で作った。ちなみに「使ってみたかった」なので、「Vue.js じゃないと作れない」というものではない。
拡張機能の紹介
ウェブストアの画像や説明が雑なのはご愛嬌ということで…
できること
ページ遷移なしに「素の URL」に付いているブコメを読み込んで表示。
もうほぼ説明することは無い(前章までに書いてある)ので、簡単にスクショで紹介。
読み込み直後
ブコメは閉じた状態、クリックすると展開される
ブコメを展開
スター数順にソートされた状態。ブックマーク追加ページへのリンクや、各ユーザーへのリンクなども表示。
ブコメが無い場合
こうなる。
できないこと
あくまで「読み込んで表示」だけなので、ページ内でブックマークしたり、コメントにスターを付けたりはできない。ただし、リンク付けてるので(面倒だけど)ページ遷移すればできるはず。
技術的な話
(初歩的な内容も含むが)拡張機能内で使っているツールや設定などを書いていく。「もっと他にいいやり方あるよ」とかあればコメント貰えると嬉しい。
リポジトリ: 17number/chrome-extension-hatiina: Chrome extension for qiita
開発環境
macBook Pro で開発した。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15 BuildVersion: 19A583 $ node -v v12.7.0 $ npm -v 6.10.3使用パッケージ
package.json
よりpackage.json"dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/free-brands-svg-icons": "^5.11.2", "@fortawesome/free-regular-svg-icons": "^5.11.2", "@fortawesome/free-solid-svg-icons": "^5.11.2", "@fortawesome/vue-fontawesome": "^0.1.7", "axios": "^0.19.0", "crx-hotreload": "^1.0.4", "moment-timezone": "^0.5.26", "vue": "^2.6.10", "webextension-polyfill": "^0.5.0" }, "devDependencies": { "@vue/cli-service": "^3.11.0", "cpx": "^1.5.0", "mkdirp2": "^1.0.4", "node-sass": "^4.12.0", "npm-run-all": "^4.1.5", "npm-watch": "^0.6.0", "rimraf": "^3.0.0", "sass-loader": "^8.0.0", "terser-webpack-plugin": "^2.1.3", "vue-svg-loader": "^0.12.0", "vue-template-compiler": "^2.6.10" }Vue.js を主ページ側で使う方法
整理すればそんなに難しいことじゃない。
(ここに辿り着くまでに割と右往左往した)
manifest.json
で先に Vue.js (の定義されたファイル)を読み込む- Vue.js のターゲットとなる HTML 要素を埋め込む
- 埋め込まれた HTML 要素に対して Vue のインスタンスを生成する
manifest.json{ ... "content_scripts": [ { "matches": [ "https://qiita.com/*/items*" ], "js": [ "js/chunk-vendors.js", # Vue.js のコードはここに入っている(ので先に読み込む) "js/app.js" # ここで Vue.js のインスタンス生成とか ], } ], ... }
app.js
(エントリポイント)app.jsimport Vue from "vue"; import App from "./App.vue"; (() => { 'use strict'; document .querySelector(".p-items > .p-items_container > .p-items_main") .insertAdjacentHTML( `afterBegin`, `<div id="hatiina"></div>` ); new Vue({ render: h => h(App), }).$mount("#hatiina"); })();この辺りの話は自分のブログに書いているので合わせて載せておく。
Chrome 拡張機能の開発中にホットリロード(自動読み込み)する方法
ビルド関連
ビルドは
vue-cli-service
で行う。Content Scripts 側と Background Scripts 側とでコマンドを分けている。Content Scripts 側
--no-clean
オプションを付けている理由は後述。package.json"scripts": { "compile:content": "vue-cli-service build --no-clean src/app.js", },ポイントは
vue.config.js
による設定で、filenameHashing
とproductionSourceMap
をfalse
にしていること。vue.config.jsmodule.exports = { filenameHashing: false, productionSourceMap: false, };
filenameHashing
の設定をしていないと、app.1234abcd.js
のようなファイル名で出力されてしまい、manifest.json
で指定するソースファイルの記述を修正しないといけなくなる。頑張れば自動で変更できると思うが、それよりはファイル名を固定にした方が良い。
productionSourceMap
の設定は、ソースマップファイルは不要だよね、というだけ。Background Scripts 側
Background Scripts 側のビルドは色々と試した結果、以下の設定だと上手く動作した。
package.json"scripts": { "compile:background": "vue-cli-service build --target lib --formats umd --dest dist/js --no-clean --name background src/js/background.js", },これで
src/js/background.js
(とそこから読み込まれているパッケージなど)が、dist/js/background.umd.js
に出力される。「なぜそのオプションにしたのか」については、何となくは分かるが明確に言語化できない(= 理解が浅い)ので、誤解がうまれるのを避けるためにも言及しない。
※よくよく考えると、別に Vue.js 関係ない(はてなブックマークの API を叩いて返すだけ)から、別にvue-cli-service
使う必要はなかったんじゃ…
--no-clean
オプションの理由以下の記事を参考に、ファイル変更を検知してホットリロードさせている。
Chrome Extensionの開発時にホットリロードさせる - Qiita
(試してないので確実ではないが)
--no-clean
を付けずにビルドして、dist
ディレクトリごと削除されると挙動がおかしくなるのでは?と思い、既存コードは残したまま上書きするようにした。自分のブログで NPM 使う設定例も書いているので、ついでに載s
Chrome 拡張機能の開発中にホットリロード(自動読み込み)する方法
その他
FontAwesome
FontAwesome は Vue.js Project で使用可能な公式パッケージがあるのでそれを利用。
Font Awesome now has an official Vue.js component that’s available for all who want to use our icons in their Vue.js projects.
Vue.js | Font Awesome より@fortawesome/vue-fontawesome の Usage に書かれている通り、
import
して Vue のインスタンス生成前にゴニョゴニョすれば、<font-awesome-icon />
というタグで表示できる。app.jsimport { library } from '@fortawesome/fontawesome-svg-core'; // ↓は使いたいアイコン // 今回は bookmark(https://fontawesome.com/icons/bookmark?style=solid) なので faBookmark import { faBookmark } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; (() => { ... // FontAwesome Setup library.add(faBookmark) Vue.component('font-awesome-icon', FontAwesomeIcon) Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount("#hatiina"); })();
:icon
で Style(Regular(far
) とか Solid(fas
) とか) とアイコン名を指定する。xxx.vue<template> <div id="app"> <font-awesome-icon :icon="['fas', 'bookmark']" /> </div> </template>以下の記事が分かりやすい。
Font awesome を Vue.js で使ってみよう - Qiita
画像・アイコン
はてなスター
はてなスターの画像は安定のいらすとやから拝借して利用。
いろいろな色の星のイラスト | かわいいフリー素材集 いらすとや
上記をダウンロードして、画像サイズを小さくした後に、TinyPNG で圧縮。
はてなブックマーク
公式素材があるのでそれを利用。SVG だったので、そのままプロジェクト内に取り込み。
拡張機能アイコン・ウェブストア画像
Canva で作成。
フォントはうずらフォントを使用
、理由はなんとなく気に入ったから。ウェブストアの説明や画像はそのうちどうにかする予定…
Hatiina のイケてない点
API Request 数が多い
はてなスター数を取ってくるのに API を連打してしまっている。これは解決手段が分かってるので、別途修正予定。
なんかスター数が多い
公式拡張機能のカウント数と見比べると分かるんだけど、なんか表示されるスター数に差がある。
とは言え、全体的に多くカウントされる(= みんな多い)だけなので、(個人的には)大した問題じゃないと思って放置予定。
ブコメ数が多いとダルい
ブコメを閉じるには、一番上の箇所をクリックするしかないんだけど、全200コメント中の100コメントあたりを見ている状態だと、閉じるにはスクロールしまくるしかない。
ウィンドウ幅を狭くした時の見た目
めっちゃ狭くするとユーザーアイコンの表示が変になったり、特定の幅で Qiita 本体のレイアウトと適合しない配置になったりする。
本質的な問題じゃないので放置。
共通化などが中途半端(な気がする)
もう少しコンポーネント分割できそうな気がする。
あと、Vue.js の(把握できていない)便利機能とかで、もっとシンプルに書けそうな気もする。
あとがき
メールとかから飛んでもブコメ見れるから、はてブ民は良ければ使ってくれよな。
※拡張機能を公開したのが 2019/10/12 なので、もしかしたら審査リジェクトで見えなくなる期間が出てくるかも。
リポジトリも公開してるから「変な拡張機能怖い」って人とか、「おかしな実装ないか調べたろ!」って人も安心だよ。
17number/chrome-extension-hatiina: Chrome extension for qiita
- 投稿日:2019-10-12T13:50:30+09:00
EC2のLaravel6.0環境でVue+Rest APIを連携する AWS/Laravel連載(10)
はじめに
前回の記事でLaravelでREST APIを作りました。
EC2のLaravel6.0環境でRest APIを準備する AWS/Laravel連載(9)
今回はAPI経由で投稿一覧表示部分をVue.js+axiosで実装します。
Vue, axiosを入れる
Laravelのpackage.jsonは最初からVue.jsとaxiosが入っているのでnpm installだけでOKです。
$ npm installVue部分を書きます。
resources/js/app.js... const app = new Vue({ el: '#app', data: { posts: [] }, methods: { fetchPosts: function(){ axios.get('/api/posts').then((res)=>{ this.posts = res.data }) } }, created() { this.fetchPosts() }, });上記保存したらビルドが必要です。
$ npm run dev終わるとpublic/js/app.jsに書き出されます。
viewを修正
resources/views/home.blade.php@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">Dashboard</div> <div class="card-body"> @if (session('status')) <div class="alert alert-success" role="alert"> {{ session('status') }} </div> @endif @{{ posts.total }}件 <table class="table"> <thead> <tr> <th>ID</th> <th>タイトル</th> <th>本文</th> </tr> </thead> <tbody> <tr v-for="post in posts.data" v-bind:key="post.id" v-cloak> <td>@{{ post.id }}</td> <td>@{{ post.title }}</td> <td>@{{ post.content }}</td></td> </tr> </tbody> </table> </div> </div> </div> </div> </div> @endsectionv-forはVue.jsでのfor文です。
app.jsの中で、APIから取ってきた投稿一覧(ページャー付き)をpostsという変数に入れています。
posts.dataの中身(配列)をループしpostという変数に入れ、件数分を表示しています。連載10.5回という形で、Vueの開発がしやすくなるツールを紹介しています。
ぜひご確認ください。
EC2のLaravel6.0環境 VueとLaravelでのTIPS AWS/Laravel連載(10.5)
- 投稿日:2019-10-12T12:01:35+09:00
SEIYU風のECサイトを作りましょう(4) Vue.js プロジェクト初期化 TypeScript使用
前回までの記事
1.Django REST framework + Vue.js「SEIYU風のECサイトを作りましょう」全6回(予定)
2.SEIYU風のECサイトを作りましょう(1)要求分析とプロジェクト初期化
3.SEIYU風のECサイトを作りましょう(2)Xadminを使って管理画面を一新します
4.SEIYU風のECサイトを作りましょう(3)Django REST frameworkで爆速APIを作りましょう前書き
このシリーズの記事を書き出した頃、フロントはjavascript使用してましたが、途中から著者がTypeScriptを習い、TypeScriptでフロント書き直すことにしました。
また、本記事はVue.js+TypeScriptの使い方を0から教えていきますので、前回までの記事を読んで無くとも楽しめると思います。
ぜひ最後までお付き合いくださいプロジェクト初期化
Vue Cliをインストール
npm install -g @vue/cli-service-global # or yarn global add @vue/cli-service-globalインストール終了後、下記のコマンドでバージョン確認できたらオーケーです。
vue -V # or vue --versionプロジェクトを生成する
管理しやすいため、バックエンド
api
フォルダと同じディレクトリで以下のコマンドを実行してください。vue create supermarketEnterキー押したらオプション選択用の画面が表示されるので、本記事と同じオプションを選んでください。
選択終了後インストールがスタートされます。
終了後、以下のコマンドを実行してください。$ cd supermarket $ yarn serveその後、表示されるurlにアクセスして、正常に以下の画面が確認できたら、オーケーです。
下準備
現在のプロジェクトのディレクトリは下記の通りです。
|- node_modules |- public |- src |- .gitignore |- babel.config.js |- package.json |- README.md |- tsconfig.json |- yarn.lockまずはログインページを作ります。
モバイル画面として見せるものであるため、いくつかの修正をします。
fastclickをインストール
TypeScriptを使用してる時にライブラリのインストールは基本ペアになります。
npm install @types/fastclick --save npm install --save fastclickfastclickライブラリの用途は、モバイルのブラウザクリック後の300msの遅延を無くすことです。
インストール終了後、
main.ts
でfastclick
を使用します。supermarket\src\main.ts... import fastClick from 'fastclick' ... (fastClick as any).attach(document.body); ...reset.cssを使用する
今回はWeb画面をスマホアプリとして見せるため、pc用のデフォルトスタイルをリセットします。
supermarket/publicの配下にcssフォルダを作って、cssの中でreset.cssを作る、そして以下の内容を貼り付けてください。reset.css@charset "utf-8";html{background-color:#fff;color:#000;font-size:12px} body,ul,ol,dl,dd,h1,h2,h3,h4,h5,h6,figure,form,fieldset,legend,input,textarea,button,p,blockquote,th,td,pre,xmp{margin:0;padding:0} body,input,textarea,button,select,pre,xmp,tt,code,kbd,samp{line-height:1.5;font-family:tahoma,arial,"Hiragino Sans GB",simsun,sans-serif} h1,h2,h3,h4,h5,h6,small,big,input,textarea,button,select{font-size:100%} h1,h2,h3,h4,h5,h6,b,strong{font-weight:normal} address,cite,dfn,em,i,optgroup,var{font-style:normal} table{border-collapse:collapse;border-spacing:0;text-align:left} caption,th{text-align:inherit} ul,ol,menu{list-style:none} fieldset,img{border:0} img,object,input,textarea,button,select{vertical-align:middle} article,aside,footer,header,section,nav,figure,figcaption,hgroup,details,menu{display:block} audio,canvas,video{display:inline-block;*display:inline;*zoom:1} blockquote:before,blockquote:after,q:before,q:after{content:"\0020"} textarea{overflow:auto;resize:vertical} input,textarea,button,select,a{outline:0 none;border: none;} button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0} mark{background-color:transparent} a,ins,s,u,del{text-decoration:none} sup,sub{vertical-align:baseline} html {overflow-x: hidden;height: 100%;font-size: 50px;-webkit-tap-highlight-color: transparent;} body {font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;color: #333;font-size: .28em;line-height: 1;-webkit-text-size-adjust: none;} hr {height: .02rem;margin: .1rem 0;border: medium none;border-top: .02rem solid #cacaca;} a {color: #25a4bb;text-decoration: none;}次にindex.htmlにリンク貼ります。
supermarket\public\index.html... <head> ... <link rel="stylesheet" href="css/reset.css"/> ... </head>最後に、ユーザーが画面の大きさを調整不可にするオプションを
meta
に追加したら、下準備は出来ました。index.html... <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> ...ログインページを作る
プロジェクトデフォルトを改造します
src
フォルダ配下にあるviews
、components
フォルダを削除し、pages
フォルダを作ります。
そしてpages
の配下にlogin
とhome
フォルダを作ります。
login
フォルダの配下にcomponents
フォルダとLogin.vue
ファイルを作ります。
home
フォルダの配下にcomponents
フォルダとHome.vue
ファイルを作ります。
完成後、ディレクトリは以下のようになります。|- src |- |- pages |- |- |- home |- |- |- |- components |- |- |- |- Home.vue |- |- |- login |- |- |- |- components |- |- |- |- Login.vue
login
配下にあるcomponents
フォルダにContent.vue
とHeader.vue
ファイルを作ります。
以下の内容を書いてください。
scriptのlangはtsに指定すること忘れないで下さい。src/pages/login/components/Content.vue<template> <div class="login-content"> login-content </div> </template> <script lang="ts"> import {Component, Vue} from "vue-property-decorator"; @Component({ components:{} }) export default class Content extends Vue{ } </script> <style scoped> </style>src/pages/login/components/Header.vue<template> <div class="login-header"> login-header </div> </template> <script> import {Component, Vue} from "vue-property-decorator"; @Component({ components:{} }) export default class Header extends Vue{ } </script> <style scoped> </style>
Login.vue
の内容は以下のようになります。src/pages/login/Login.vue<template> <div class="login"> <login-header></login-header> <login-content></login-content> </div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; import LoginHeader from "./components/Header.vue"; import LoginContent from "./components/Content.vue"; @Component({ components:{ LoginHeader, LoginContent } }) export default class Login extends Vue{ } </script> <style scoped> </style>
Home.vue
は構造だけ書いておきます。src/pages/home/Home.vue<template> <div class="home"> home </div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; @Component({ components:{} }) export default class Login extends Vue{ } </script> <style scoped> </style>
router.ts
ファイルを以下のように修正します。src/router.tsimport Vue from 'vue' import Router from 'vue-router' import Home from './pages/home/Home.vue' Vue.use(Router) export default new Router({ mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home }, { path: '/login', name: 'login', component: () => import('./pages/login/Login.vue') } ] })サーバーを立ち上げて動作を見てみましょう。
npm run serveログインページの詳細を書いていきます
先ずは
Content.vue
からいきます。
デザインに関しては得意ではないため、こうした方がいいでしょう、と思う所があれば、全然変えて大丈夫です。src/pages/login/components/Content.vue<template> <div class="login-content"> <div class="logo"> <img class="logoimg" src="data:image/jpeg;base64,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" alt=""> </div> <div class="itemContent"> <div class="login"> <div class="input-container"> <input type="text" class="username" v-model="userName" placeholder="ユーザーネーム/アドレス"> </div> <div class="input-container"> <input type="password" class="username" v-model="passWord" placeholder="パスワード"> <div class="agin">パスワード再発行</div> </div> </div> <div class="loginbtn" @click="loginfuc">ログイン</div> <div class="registration"> <div class="registerbtn">新規登録</div> </div> <div class="logintype"> <div class="quick-login"> <h4 class="txt-otherLogin">それ以外のログイン</h4> <a href="" class="icon">GitHub</a> <a href="" class="icon">Qiita</a> </div> <div class="agreement-tips"> <p>https://github.com/huanshenyi/qiita-Django-supermarket</p> </div> </div> </div> </div> </template> <script lang="ts"> import {Component, Vue, Provide} from "vue-property-decorator"; @Component({ components:{} }) export default class Content extends Vue{ @Provide() userName:string = ""; @Provide() passWord:string = "" ; loginfuc () { console.log("ログインが推された"); (this as any).$router.push("/") } } </script> <style scoped lang="stylus"> .login-content padding: 0 .25rem .25rem; .logo width 100% height 20% text-align center .logoimg width 2rem height 2rem .itemContent max-width: 7.5rem; margin: 0 auto; .input-container height: 100%; padding-top: .2rem; padding-bottom: .2rem; position: relative; overflow: hidden; margin-top: .4rem; background: #fff; border-bottom: .01rem solid #efefef; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; .agin border-left: 1px solid #ccc; float right font-size: .2rem; padding-left: .16rem; .username float left padding: 0 .3rem 0 0; font-size: .3rem; line-height: normal; border-radius: 0; border: 0; .loginbtn margin-top .8rem width: 100%; height: .8rem; line-height: .8rem; display: block; background-color: #efefef; border-radius: .25rem; font-size: .36rem; color: #fff; background-image: linear-gradient(90deg,#fab3b3,#ffbcb3 73%,#ffcaba); box-shadow: 0 0.1rem 0.2rem 0 rgba(255,62,62,.2); text-align: center; font-family: PingFangSC-Semibold; .registration margin-top: .2rem; text-align: center; .registerbtn width: 100%; height: .8rem; line-height: .8rem; display: block; border-radius: .25rem; box-sizing: border-box; border: 1px solid #ff2000; color: #f10000; background: #fff; margin-top: .1rem; .logintype margin-top: .88rem; .quick-login height: .8rem; position: relative; padding: .5rem 0 0; border-top: 1px solid #efefef; text-align: center; .txt-otherLogin font-size: .3rem; font-weight: 400; position: absolute; top: -.14rem; left: 50%; background-color: #fff; padding: .05rem .15rem; color: #ccc; -webkit-transform: translateX(-50%); transform: translateX(-50%); .icon position: relative; width: .48rem; height: .48rem; color: #616161; margin: 0 .15rem; font-size: .14rem; .agreement-tips margin-top: .12rem; text-align: center; font-size: .3rem; color: #bebebe; </style>次は
Header.vue
を書いていきます。src/pages/login/components/Header.vue<template> <div class="login-header"> <div class="headerLeft"> <router-link to="/"> < </router-link> </div> <div class="headerTitle"> <div class="title">ログイン</div> </div> </div> </template> <script> import {Component, Vue} from "vue-property-decorator"; @Component({ components:{} }) export default class Header extends Vue{ } </script> <style scoped lang="stylus"> .login-header display flex height .86rem line-height .86rem border-bottom: 1px solid #e5e5e5; width: 100%; position: relative .headerLeft margin-left .3rem float left .headerTitle height .64rem font-size .4rem padding-left 2.5rem </style>修正後、画面の変化を見てみましょう。
下記の画像の通りに表示できたらオーケーです。
次回予告
最後まで読んでいただいてありがとうございます。
次回では
JWT
を使って実際ログイン機能を完成しようと思います。
- 投稿日:2019-10-12T11:50:29+09:00
なぜ state を直接変更すると再描画されないのか
意外と state の挙動の理由について知らない人が多いと思ったので、勉強ついでに少しまとめてみようと思います。
state の挙動について
state には以下の特徴があります。
- state は
Object.assign()
やスプレッド演算子(...Object)
として 値を渡さないとうまく変更が反映されない- state を直接変更すると再描画が起こらない
- state を他の変数に入れて、その変数を変更して state に再代入しても再描画が起こらない(または挙動がおかしくなる)
これらの挙動はフレームワーク固有の特徴ではなく Javascript 自体の特徴です!
Javascript の Object の挙動について
まず state の挙動を理解する上で一番理解しなければいけないのは Javascript 自体の挙動についてです。 state は基本的に Object で管理されています。(React の Hooks でも Object で管理する場合は同じです)
この Object がかなり重要です。Objectの特徴は以下の通りです.
- Object 同士で比較する場合、メモリを比較するため Object の中身を比較しているわけではない
- 値は参照渡しで代入される
これらが Object の特徴であり、理解しなければいけない点です。
まず1の Object 同士で比較する場合、メモリを比較するため Object の中身を比較しているわけではない について実際にコードで見ていきます。
example1.jsconst a = {key: 'test'}; const b = {key: 'test'}; /* Object を比較する方法は2通りあります。 */ console.log(a === b); // result: false console.log(Object.is(a, b)); // result: falseこのように Object はメモリの比較によって結果が求められるので変数が異なれば、値が同じでも
false
になります。次に2の 値は参照渡しで代入される について見ていきます。
example2.jsconst a = {key: 'test'}; const b = a; b.key = 'qiita'; console.log(a === b); // result: true console.log(Object.is(a, b)); // result: true console.log(`a: ${a.key}, b: ${b.key}`); // a: qiita, b: qiitaこのように参照渡しで値が渡されるため
変数a
と変数b
を比較した場合に、結果がtrue
になります。結論
ここまで見てきた通り、Object は参照渡しによって値が代入されて、メモリごとの比較になるため、いちいち新しい Object を生成しなければ Object の変更が検知されないということになります。
つまり、state を直接変更した際には Object の変更が検知されていないために再描画が行われていない ということになります。
また React ではshallow equal
という比較が行われているため、直接 state を変更しても再描画が行われることがありますが、挙動としては正しくないため、stateの変更をするときは新しく Object を生成するということを意識して開発しなければ予期せぬバグを産む可能性があります。間違いや補足があれば指摘していただけたら嬉しいです。
最後まで読んでいただきありがとうございました。
- 投稿日:2019-10-12T10:46:19+09:00
Vuexの利用方法メモ
書いてあること
- Vuexの利用方法を試した際のメモ
参考
環境
- CentOS Linux release 7.6.1810 (Core)
- Node.js v10.16.0
- Npm 6.10.0
- Vue 2.6.10
- Vuex 3.0.1
レポジトリ
用語
ステート(State)
:アプリケーション全体の状態を保持するオブジェクトゲッター(Getters)
:ステートから別の値を算出するミューテーション(Mutations)
:ステートの値を更新するアクション(Actions)
:非同期処理や外部APIと通信を行い、ミューテーションによりステートを更新する事前準備
Vue CLIでVuexインストール済の場合は事前準備作業は不要。
インストール
bashnpm install -y vuexVuex利用設定
store.jsimport Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // ストアの定義 const store = new Vuex.Store({ // ステート、ミューテーションなどをここに記載 }) // ストアをエクスポート export default storemain.jsimport Vue from 'vue' import Default from './layouts/default.vue' import router from './router' +import store from './store' import vuetify from './plugins/vuetify' Vue.config.productionTip = false Vue.prototype.$axios = axios new Vue({ router, + store, // コンポーネントからストアを利用可能にする vuetify, render: h => h(Default), }).$mount('#app')ステート
定義
src/store/index.jsconst store = new Vuex.Store({ state: { ●●●: 内容, }, }利用
算出プロパティでステートを自身の
this.●●●
に紐づけて利用する。<script> export default { computed: { ●●●() { return this.$store.state.●●● } } </script>ゲッター
定義
src/store/index.jsconst store = new Vuex.Store({ getters: { ●●●(state) { return 処理内容 }, }, }利用
算出プロパティでステートを自身の
this.●●●
に紐づけて利用する。<script> export default { computed: { ●●●() { return this.$store.getters.●●● } } </script>ミューテーション
定義
src/store/index.jsconst store = new Vuex.Store({ mutations: { ●●●(state, payload) { ステートの更新処理など }, }, }利用
メソッドからミューテーションを利用する。
<script> export default { methods: { ●●●() { this.$store.commit('●●●', payload) }, }, } </script>アクション
定義
src/store/index.jsconst store = new Vuex.Store({ actions: { ●●●(state, payload) { 非同期処理や外部APIと通信など }, }, }利用
メソッドからアクションを利用する。
<script> export default { methods: { ●●●() { this.$store.dispatch('●●●', payload) }, }, } </script>モジュール
名前空間を有効化(モジュール分割)
namespaced: true利用方法の違い
ルートストア、モジュール分割なし(namespaced: false)、モジュール分割あり(namespaced: true)で下記の通り利用方法が異なる
種類 ルートストア namespaced: false namespaced: true ステート this.$store.state.●●● this.$store.state.■■■.●●● this.$store.state.■■■.●●● ゲッター this.$store.getters.●●● this.$store.getters.●●● this.$store.getters[■■■/●●●] ミューテーション this.$store.commit('●●●', payload) this.$store.commit('●●●', payload) this.$store.commit('■■■/●●●', payload) アクション this.$store.dispatch('●●●', payload) this.$store.dispatch('●●●', payload) this.$store.dispatch('■■■/●●●', payload) ルートストアの利用
モジュール分割したストアからルートストアを利用することが可能。
ステート
<script> export default { getters: { ●●●(state, getters, rootState, rootGetters) { rootState.●●●を利用 }, }, } </script>ゲッター
<script> export default { getters: { ●●●(state, getters, rootState, rootGetters) { rootGetters.●●●を利用 }, }, } </script>ミューテーション
<script> export default { methods: { ●●●({ commit }, payload) { commit('●●●', payload, { root: true }) }, }, } </script>アクション
<script> export default { methods: { ●●●({ dispatch }, payload) { dispatch('●●●', payload, { root: true }) }, }, } </script>ヘルパー関数
ヘルパー関数によりストアの各要素をコンポーネントの算出プロパティ・メソッドに紐付けることができる
ステート
import { mapState, createNamespacedHelpers } from 'vuex' const ■■■Helpers = createNamespacedHelpers('■■■') export default { computed: { ...mapState(['●●●']), ...mapState({ ●●●: '●●●' }), ...mapState('■■■', ['●●●']), ...mapState('■■■', { ●●●: '●●●' }), ...■■■Helpers.mapState(['●●●']), ...■■■Helpers.mapState({ ●●●: '●●●' }), }, }ゲッター
import {mapGetters, createNamespacedHelpers } from 'vuex' const ■■■Helpers = createNamespacedHelpers('■■■') export default { computed: { ...mapGetters(['●●●']), ...mapGetters({ ●●●: '●●●' }), ...mapGetters('■■■', ['●●●']), ...mapGetters('■■■', { ●●●: '●●●' }), ...■■■Helpers.mapGetters(['●●●']), ...■■■Helpers.mapGetters({ ●●●: '●●●' }), }, }ミューテーション
import { mapMutations, createNamespacedHelpers } from 'vuex' const ■■■Helpers = createNamespacedHelpers('■■■') export default { methods: { ...mapMutations(['●●●']), ...mapMutations({ ●●●: '●●●' }), ...mapMutations('■■■', ['●●●']), ...mapMutations('■■■', { ●●●: '●●●' }), ...■■■Helpers.mapMutations(['●●●']), ...■■■Helpers.mapMutations({ ●●●: '●●●' }), }, }アクション
import { mapActions, createNamespacedHelpers } from 'vuex' const ■■■Helpers = createNamespacedHelpers('■■■') export default { methods: { ...mapActions(['●●●']), ...mapActions({ ●●●: '●●●' }), ...mapActions('■■■', ['●●●']), ...mapActions('■■■', { ●●●: '●●●' }), ...■■■Helpers.mapActions(['●●●']), ...■■■Helpers.mapActions({ ●●●: '●●●' }), }, }VueRouter連携
vue-router-syncをインストール
bashnpm install -y vuex-router-sync --saveRouterとStoreを同期
main.jsimport Vue from 'vue' import Default from './layouts/default.vue' import router from './router' import store from './store/index' +import { sync } from 'vuex-router-sync' import axios from 'axios' import vuetify from './plugins/vuetify' Vue.config.productionTip = false // RouterとStoreを同期 +sync(store, router) Vue.prototype.$axios = axios new Vue({ router, store, vuetify, render: h => h(Default), }).$mount('#app')ストアからRouter情報の取得
vue-router-syncによってルートに紐付いた情報を取得する
コンポーネントから利用
export default { computed: { ●●●() { return this.$store.state.route }, }, }他のストアから利用
<script> export default { getters: { ●●●(state, getters, rootState) { return rootState.route }, }, } </script>
- 投稿日:2019-10-12T10:39:56+09:00
v-ifの条件式で比較演算子(==)を使いたいメモ
すごい簡単なことだが、
Vue.jsのv-if
の条件式で比較演算子(==)を使用したいと思ったところ、
公式を見てもわかりにくかったのでメモ。コードを見れば一発
<template> <div> <button v-if="this.id == 1">新規作成</button> </div> </template> <script> export default { name: "TestBtn", data: function(){ return { id: 1 } }, }; </script>※コードは一部抜粋
vi-ifの中で条件式を書けばいいだけ。
権限が1なら、新規作成ができるみたいなイメージ。これだけじゃ、味気ないので、
ボタンをクリックしたら、idを変更して、
新規作成ボタンが非表示になることも確認。<template> <div> <button v-if="this.id == 1" v-on:click="changeId">新規作成</button> </div> </template> <script> export default { name: "TestBtn", data: function(){ return { id: 1 } }, methods: { changeRoleId: function() { this.id = 2; } } }; </script>リアクティブに動いていることを確認できました〜
- 投稿日:2019-10-12T06:42:44+09:00
plunkerでvue その42
概要
plunkerでvueやってみた。
練習問題やってみた。練習問題
canvasを使え。
成果物
https://embed.plnkr.co/sJ0fpkeUWYC1iossgZyS/
https://embed.plnkr.co/2L2BzPLC8ivlrIIts0ig/
https://embed.plnkr.co/YpMdqlIAqFkdoMljZdla/
以上。