- 投稿日:2020-07-03T22:43:24+09:00
なぜ、Vue Composition APIを使うのか、理解する
はじめに
Vue3のリリースが迫ってきました。
順調に行けば、公式リリースは8月上旬のようです。楽しみですね。Our current target dates are mid July for the RC (release candidate) and early August for the official release of 3.0.
https://github.com/vuejs/rfcs/issues/183Vue3の目玉の機能として、「Composition API」があります。
この記事では、「”なぜ” “なんのために” Composition APIを使うのか」理由について、まとめています。
「Composition APIの使い方」についての説明は、この記事では割愛します。
Composition APIを使う理由、なんもわからん
これまで、私自身、Composition APIを使う理由が、よくわかっていませんでした。
なんとなく、「setup関数でまとめて処理を登録しておく」くらいのことを雰囲気で理解していました。
「Composition APIで書くほうがイケてるし、カッコいい(重要)」くらいのノリで
何のために使うのか、メリットがよく分からない状態でした。
Composition APIについて調べていると、↑の図、よく見ませんか?
既存APIとComposition APIのコードを比較して、メリットを提示しているようですけど
私は
「そんなに違わないように見える」
「違いがよくわからん」
っといった感想を持ちました。
私と同じように
「Vueに新しく、Composition APIってのが出るらしいけど、なんとなく使おうかな〜」
「でも、なんのために使うんだっけ」
と思っている方が多いのではないかなと考え、この記事を執筆しています。
この記事が、誰かのお役に立てれば幸いです。
Composition API RFC を読んだら、「なぜ、使うのか」を理解した
先日 @shwld が主催した
「Vue Composition APIを知る会」
で、Composition APIのRFCを読む機会があり、そこで改めて「”なぜ” Composition APIを使うのか」理解することができました。
Composition APIの公式の説明資料として、以下のページにRFCがあります。
Composition API (https://vue-composition-api-rfc.netlify.com)↑を読むと、「”なぜ” Composition APIを使うのか」答えが書いてありました。
以下にRFCに書いてある内容をまとめます。
Composition APIとは
Composition APIは、
ロジックを合成関数(composition function)にカプセル化することで、コンポーネント間でのロジックを再利用を可能にするAPIです。
(React HooksのVue版みたいなもの)サンプルコード
<template> <button @click="increment"> Count is: {{ state.count }}, double is: {{ state.double }} </button> </template> <script> import { reactive, computed } from 'vue' export default { setup() { const state = reactive({ count: 0, double: computed(() => state.count * 2) }) function increment() { state.count++ } return { state, increment } } } </script>Vueの作成者であるEvan You氏いわく、Composition APIを提案する背景となった動機について、次のように説明しています。
論理合成(logic composition)は、プロジェクトをスケールアップする場合には、おそらく最も深刻な問題のひとつです。さまざまなタイプのプロジェクトを扱うユーザは、さまざまなニーズに直面します。その中には、オブジェクトベースのAPIを使用して簡単に処理できるものと、できないものがあります。主な例としては、
1. 複数の論理タスクをカプセル化した、大規模な(数百行の)コンポーネント
2. 複数のコンポーネント間においてタスクにロジックを共有するニーズhttps://www.infoq.com/jp/news/2019/10/vue3-function-based-api-rfc/ より引用
”なぜ” Composition APIを使うのか
結論から言いますと、
「”なぜ” Composition APIを使うのか」
その理由は、
「ロジックの抽出と再利用」をするためです。
(他にも「TypeScript 型強化」などが理由として挙げられますが、この記事では割愛します)
ロジックの抽出と再利用
「ロジックの抽出と再利用」は、なぜするのか。
「ロジックの抽出と再利用」が必要な理由は、
複雑に肥大化したコンポーネントを、小分けにして関心事で分別し、クリーンな状態に整理するためです。近年、Vue を使用して大規模なプロジェクトを構築することが増えました。
肥大化してメンテしにくいコンポーネントを目の当たりにして、我々エンジニアが苦しむことも増えました。
肥大化したコンポーネントのコードは、依存関係が複雑で量も多く、コードを読むこと自体難しくなります。
これまでのVue2.xでは、「複数のコンポーネント間でロジックを抽出して再利用するためのクリーンでコストのかからないメカニズム」が欠如していました。
これを解決するのが、Composition APIです。
Composition APIによって、コンポーネントのコードを整理する際に、開発者により高い柔軟性を提供します。
コンポーネント間で、ロジックを抽出して再利用することが、簡単になります。つまり、Composition APIは、肥大化したコンポーネントを小さく切り刻むための聖剣なのです。
Composition APIを使って「ロジックを抽出して再利用する」具体的なやり方について、以下の記事が参考になります。
vue-composition-apiを使って機能単位のコード分割と合成を試してみた
https://qiita.com/s_nagasawa/items/ef70032f996face318e5逆に言えば、100行にも満たないような小さなコンポーネントをComposition APIで書き直しても、あまりメリットを享受できないです。
もし小規模〜中規模なVueプロジェクトにおいて、現状のVue2.xベースのコードでコンポーネントの肥大化に苦しんでいないのであれば、Composition APIで書き直してもコスパが悪いのでやめておいたほうがよいのではないかなと思います。
(新規プロダクトで、最初から全部Composition APIで書くのはありだと思います)mixinは、使えない?
Vue2.xのロジックの再利用の仕組みとして、「mixin」があります。
しかし、「mixin」には、罠が多くあり、今では「mixin」を使うことはアンチパターンという認識です。
極力「mixin」は使わないようにしましょう。なぜmixinがアンチパターンなのかについては、下記の記事に詳しく書かれてあります。
俺がやらかしたVue mixinのアンチパターンから学ぶmixinの使い方と代替案
https://aloerina01.github.io/blog/2018-12-25-1Composition APIを使う際のデメリット
Composition APIを使うメリットは、理解できたかと思います。
じゃあ、デメリットはないのか?
あります。
デメリットを一言でいうと、「自由すぎ」です。
Composition APIによって、自由にロジックを切り出せるようになりますが、一方で、いままでVueが暗黙的に行ってくれていたレールから外れることを意味します。
どういう単位でロジックを切り出したり、共通化したり、といった疑問に対して、明確な正解がないのです。
つまり、Vueが管理している壁の中の世界から、壁の外の無秩序な荒野へと放り出されるのです。
無秩序な荒野で、テキトーに開発していると、隠れていた猛獣に襲われたり、食料が尽きたりといった致命的なアクシデントに見舞われます。計画的に外の世界を開拓しないと危険がいっぱいなのです。
「設計」や「アーキテクチャ」が重要になる
その荒野に秩序を与えるのは、我々、調査兵団(エンジニア)の「設計」です。
今後は、「アーキテクチャ」が重要になります。
つまり、デメリットは、ちゃんと「設計」や「アーキテクチャ」を考えなければならないことです。
例えば、「クリーンアーキテクチャ」を理解して、実装していくことが求められます。
実装クリーンアーキテクチャ
https://qiita.com/nrslib/items/a5f902c4defc83bd46b8「Vueの中の世界」と「外の世界」を意識して、自由への翼を獲得しましょう。
まとめ
・https://vue-composition-api-rfc.netlify.com を読もう。
・Composition APIを使う理由は、「ロジックの抽出と再利用」のため。(TypeScriptの型強化の側面もあり)
・肥大化したコンポーネントを、小さく切り刻むための聖剣だと理解した。(mixin、お前はダメだ)
・もともと小さいコンポーネントをCompositon APIで書き直しても、あまりメリットはない。
・今後はちゃんと「設計」や「アーキテクチャ」を考えなければならない。
参考リンク
Composition API RFC
https://vue-composition-api-rfc.netlify.comAPI Reference
https://composition-api.vuejs.org/api.htmlComposition API Demo
https://github.com/LinusBorg/composition-api-demosvue-composition-apiを使って機能単位のコード分割と合成を試してみた
https://qiita.com/s_nagasawa/items/ef70032f996face318e5俺がやらかしたVue mixinのアンチパターンから学ぶmixinの使い方と代替案
https://aloerina01.github.io/blog/2018-12-25-1実装クリーンアーキテクチャ
https://qiita.com/nrslib/items/a5f902c4defc83bd46b8【Vue3に備える】実務で使うComposition APIについて考える
https://medium.com/finatext/composition-api-for-vue3-63631dbadcef先取りVue 3.x !! Composition API を試してみる
https://qiita.com/ryo2132/items/f055679e9974dbc3f977Vue Composition API v1-beta で使えるリアクティブ関連のAPI一覧
https://qiita.com/ryo2132/items/6dc51ede8082dea75812Vue Composition API を使ったストアパターンと TypeScript の組み合わせはどのくらいスケールするか?
https://qiita.com/tmy/items/a545e44100247c364a71Composition API 勘所など
https://webneko.dev/posts/notices-of-composition-api-in-vue3-eve
- 投稿日:2020-07-03T19:24:26+09:00
VueでRailsのactive_storageに保存した画像を表示する
Railsアプリ内でVueを使用し、SPAを実装しました。
その中で、VueからRailsのactive_storageを使おうと思いましたがそれっぽい方法を見つけられず、自分でとりあえず実装してみたので残します。
プログラミング初心者かつVue自体も初めてなので間違いもだいぶあると思います。見つけたら指摘していただいたら嬉しいです。version
Rails 6.0.3.2
ruby 2.6.3
@vue/cli 4.4.1active_storageに保存するまでは
https://qiita.com/ozin/items/5ec81a4b126b8ebf7a96
この記事でやりました。このあとvue側に表示するまでを書きます。Vueでは単一ファイルコンポーネントで実装しています。
ライブラリのインストール方法は省略します。モデル
diary.rbhas_one_attached :image # 省略コントローラ
diaries_controller.rbdef index diaries = Diary.all diaries_array = [] diaries.each do |d| hash = d.attributes # インスタンスをハッシュにする if d.image.attached? # imageのパスを取得 image = Rails.application.routes.url_helpers.rails_blob_path(d.image) hash["image"] = image # ハッシュに新しいkeyと値を追加 else hash["image"] = '' end diaries_array << hash # 配列にハッシュを要素として追加 end render json: diaries_array endaxiosでリクエストを送る
diareis.vue// 省略 <script> export default { data: function() { return { diaries: '' } }, mounted() { let that = this; axios.get('/APIなどのパス/').then(function(response) { that.diaries = response.data; } } } <script>バインドして表示
diaries.vue<template> <div> <div v-for='diary in diaries' v-bind:key='diary.id' > <div v-if='diary.image'> <img v-bind:src="diary.image"> </div> </div> </div> </template>これで表示できました。
指摘などありましたらお願いします!
- 投稿日:2020-07-03T18:04:06+09:00
【Vueで使える】差がつくCSS画像アニメーションまとめ49選
こちらの記事に記載のデザイン・コードは全て自由に使っていただいて大丈夫です(筆者が作成したため)
プロジェクトに取り込んでより充実したデザインにしてもらえれば○
*動きだけ確認したい初学者の方はJSFiddle使ってみるといいですよ
1. hoverで画像が拡大(ズーム)するCSSアニメーション3選(+filterエフェクト)
2. box-shadowとhoverで3D画像に動きをつけるCSSアニメーション3選!
3. hoverとtransitionでオシャレなCSS画像エフェクト4選!(背景がスっと表示される)
4. CSSだけ!hoverとtransitionで作る動的画像エフェクト4選(コピペOK)
5. hoverとfilter:grayscaleで白黒に切り替え!CSS画像エフェクト3選
6. hoverとtransformで画像が回転するCSSアニメーション3選【3分で作れる!】
7. hoverで画像がスライド!margin-leftとscaleでCSSアニメーション3選
](https://www.twinzlabo.com/css-animation-hover-slide-transition/)
8. hoverとfilter:brightnessで明るさ調節!CSS画像エフェクト3選
9. overflow: hiddenで美しい!CSSアニメーション3選(画像一覧)
10. 【CSSだけ】filterとopacityで洗練されたスライダーアニメーション3選
11. hoverで画像が鏡みたいに反射!position:absoluteとfilter多用でCSSアニメーション3選
12. hoverで画像の色が暴れ出す!filter: hue-rotateとtransitionでCSSエフェクト3選
](https://www.twinzlabo.com/css-effects-hover-filter-hue-rotate/)
13. transitionとfilterで美しく変化するCSS画像エフェクト5選(基礎から応用まで)
14. hoverでぼかし画像が動き出す!filter:blurとtransitionでCSSエフェクト3選
15. 【filterプロパティ】hoverすると美しさ100倍!CSS画像エフェクト3選
いかがでしたでしょうか?
お役に立てたら嬉しいです。最後に注意書きとして
コメント欄に心ないコメントを投稿する方がまれにいますが迷惑なので問答無用で速攻ブロックします。
- 投稿日:2020-07-03T18:03:33+09:00
`Vue packages version mismatch` に対する対処メモ
Nuxt.js+Firebaseを勉強しています。
npm audit fix
したら、ビルドがVue packages version mismatch
エラーになりました。本記事はそのときのメモです。環境
- Firebase 7.3.0 → 7.15.5
- Vue 2.6.10 → 2.6.11
背景
サードパーティのバリデーションプラグインであるVee-validateをインストールすることにしました。
npm install vee-validate --save
したときに次のようなメッセージが表示されました。added 1 package from 1 contributor and audited 1300 packages in 66.018s found 197 vulnerabilities (181 low, 16 moderate) run `npm audit fix` to fix them, or `npm audit` for details脆弱性が疑われるライブラリバージョンを使っていると警告が出るとのことなので、
npm audit fix
を実行しました。
npm audit fix
は成功し、 Firebase 7.3.0 が Firebase 7.15.5 になりました。ビルド時のエラー
npm run build
を実行したときのエラーは次の通りです。Vue packages version mismatch: - vue@2.6.10 - vue-server-renderer@2.6.11 This may cause things to work incorrectly. Make sure to use the same version for both.対処
以下、私の判断が正しいのか分かりませんが。。。
私はvueのバージョンを上げるべきと判断して
npm i vue
を実行しました。実行結果は以下の通りです。+ vue@2.6.11 removed 1 package, updated 1 package and audited 1165 packages in 13.653s found 0 vulnerabilities
vue-server-renderer@2.6.11
とバージョンが同じになりました。再度
npm run build
を実行したところ、さらにエラーが出ました。内容は次の通りです。Vue packages version mismatch: - vue@2.6.11 (xxx\node_modules\vue\dist\vue.runtime.common.js) - vue-template-compiler@2.6.10 (xxx\node_modules\vue-template-compiler\package.json) This may cause things to work incorrectly. Make sure to use the same version for both. If you are using vue-loader@>=10.0, simply update vue-template-compiler. If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump vue-template-compiler to the latest.
npm info vue-loader version
でvue-loaderのバージョンを確認したところ10.0以上だったので、npm install --save-dev vue-template-compiler
でvue-template-compilerのバージョンを上げます。+ vue-template-compiler@2.6.11 removed 1 package, updated 1 package and audited 1169 packages in 9.933s found 0 vulnerabilities
vue@2.6.11
とバージョンが同じになりました。
npm run build
を実行すると無事に成功しました。
- 投稿日:2020-07-03T17:10:25+09:00
【Nuxt.js】Nuxt文法編:v-bind
? この記事はWP専用です
https://wp.me/pc9NHC-nh前置き
Nuxt.js Vue.js を使う上で必須の知識です!
これができるとコンポーネントを活かして
表現の幅を広げることができます?例えば!
親によってクラスの有無を切り替えて
色分けをする、といったことができます?基本的な使い方
v-bind:{ 属性名 }="{ 変数 }"
タグの属性を変数にしたい時に使います。
aタグのhref属性をv-bindしてみます。飛びたいリンク先
http://localhost:3000/index.vue<template> <div class="page"> <a v-bind:href="url">リンク</a> </div> </template> <script> export default { data () { return { url: '/', } }, } </script>? この記事はWP専用です
続きはWPでご覧ください?
https://wp.me/pc9NHC-nh
- 投稿日:2020-07-03T16:52:28+09:00
Vue.jsプロジェクト開発を始める
はじめに
前回、Vue.js開発環境構築記録という記事を書きました。
こちらではvue-cli
をインストールするまでを目標としました。
今回はその続きです。実際にプロジェクトの開発環境が整うまでの手順を記録します。目標
プロジェクトを一つ作成してブラウザで実行できたことを確認する。
やったこと
IDEの用意
何を使うのか迷いましたが Visual Studio Code をインストールしてみることにしました。
理由としては
- Azure DevOps を使える環境であるため
- 前の現場で少しだけ触ったがまだ慣れてはいないため
- 各所でオススメされているため
などの理由で決めました。
ダウンロードページからMac用を入手。
ダウンロードフォルダに Visual Studio Code.app が保存されて、それをクリックするだけでIDEが立ち上がりました。プロジェクトの初期化
公式サイトにvue-cliのはじめ方が乗っていました。https://jp.vuejs.org/2015/12/28/vue-cli/
使用方法は次のようになります:
npm install -g vue-cli
vue init webpack my-project
プロンプトへ回答
cd my-project
npm install
npm run dev # ドジャーン!すごく簡単そうに見えます。
この手順に沿ってやってみます。npm install -g vue-cli
これは前回の記事を書いたときに実施済みです。
vue init webpack my-project
自分のworkspace内で実行します。
$ vue init webpack for-me Command vue init requires a global addon to be installed. Please run npm install -g @vue/cli-init and try again.言われるがまま
$ npm install -g @vue/cli-init npm WARN deprecated vue-cli@2.9.6: This package has been deprecated in favour of @vue/cli npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142 npm WARN deprecated coffee-script@1.12.7: CoffeeScript on NPM has moved to "coffeescript" (no hyphen) + @vue/cli-init@4.4.6 added 253 packages from 207 contributors in 15.257sもう一回
$ vue init webpack my-projectプロンプトへ回答
上のコマンドを実行するとプロンプトでプロジェクトの設定方法を問われます。
回答形式はいくつかありました。
- (Y / n)を押して選択
- 矢印キーで選択してEnter
? Project name my-project ? Project description A Vue.js project ? Author ? Vue build standalone ? Install vue-router? Yes ? Use ESLint to lint your code? Yes ? Pick an ESLint preset Standard ? Set up unit tests Yes ? Pick a test runner jest ? Setup e2e tests with Nightwatch? No ? Should we run `npm install` for you after the project has been created? (recommended) npm質問が終わるとプロジェクトの作成が始まります。
終了すると、実行ディレクトリ/プロジェクト名
配下にファイルが作成されました。
cd my-project
cd my-projectnpm install
npm install audited 1744 packages in 10.551s 29 packages are looking for funding run `npm fund` for details found 580 vulnerabilities (566 low, 9 moderate, 5 high) run `npm audit fix` to fix them, or `npm audit` for detailsnpm run dev
$ npm run dev DONE Compiled successfully in 6781ms 16:13:48 I Your application is running here: http://localhost:8080停止する場合は
Control + c
Visual Studio Code で開いてみる
Welcome pageのOpen folderから先ほどinitしたフォルダを選択します。
さいごに
開発を始める準備がようやく整いました。
ここから実際にソースを触るのはまた別の記事に残します。助けてもらったサイト
- 投稿日:2020-07-03T16:41:33+09:00
【Vue】漫画の進捗管理ツール作ってみた
あと何コマ?
コマ数で漫画の進捗管理するツールを作りました。
コマ数を入力するとコマが出てきて、
終わったコマをクリックすると塗りつぶされて完了状態になります。
1コマ辺りの作業時間を入力すると、残りの作業時間がわかります。
1コマの作業時間×残りコマ数で残りの作業時間が算出される訳ですね。何故作ろうと思ったのか
ページ単位で管理するツールは既にあるが、
コマ単位で管理するものはなかったため。
漫画制作のモチベーションを維持するためにこういうツールがほしかった。
(コマ単位で管理しないとモチベが保たない)
Vue初心者が悶絶しながら作ったものですが、この記事が他の勉強中の方の参考になればと思います。ソース全文
panels.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>あと何コマ?</title> <!-- Bootstrap --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> <!-- fontawesome --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha256-UzFD2WYH2U1dQpKDjjZK72VtPeWP50NoJjd26rnAdUI=" crossorigin="anonymous" /> <link rel="stylesheet" href="panels.css"> </head> <body> <header class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4"> <nav class="mt-2 my-md-0 mr-md-3"> <a class="px-2 text-white" href="#paneldiv">あと何コマ?</a> <a class="px-2 text-white" href="#timediv">あと何分?</a> </nav> </header> <div class="jumbotron jumbotron-fluid"> <div class="container captionText"> <p>コマ数で進捗管理するツールです。残りコマ数を入力して、入力完了ボタンを押してください。<br> 1コマ辺りの作業時間を入力すると、残りの作業時間がわかります。 </p> </div> </div> <div id="app" class="mb-5"> <div class="container main py-4 mt-sm-3"> <article class="text-center pt-3 pb-4" id="paneldiv"> <div class="alertArea text-center mb-3"> <strong v-show="alertShow"> コマ数を入力してください </strong> </div> <h3 v-if="!resultShow"> あと<input type="number" v-model.number="remainedPanelsInput" min="0" class="panelInput">コマ? <button type="button" class="ml-2 btn page-link text-light d-inline-block btn-purple" @click="resultShowFunc" v-if="!resultShow">入力完了</button> </h3> <h3 v-else> 全<input type="number" v-model.number="remainedPanelsInput" min="0" class="panelInput">コマ <button type="button" class="ml-2 btn page-link text-light d-inline-block btn-purple" @click="resultReset" v-if="resultShow">リセット</button> </h3> <div v-show="resultShow"> 全{{ remainedPanelsInput }}コマ- 済み<input type="number" v-model.number="filledPanels" v-bind:max="remainedPanelsInput" min="0">コマ = あと<span class="resultText">{{ remainedPanelsNumber }}</span>コマ <p class="text-muted pt-3">終わったコマをクリックすると、塗りつぶされて完了状態になります。完了状態のコマをクリックすると未完の状態に戻ります。</p> </div> <section class="row pricing-header px-3 py-3 pt-md-3 pb-md-1 mx-auto text-center" v-show="resultShow"> <div class="panel" v-for="n in remainedPanelsNumber" @click="panelFinished"> <div class="panelInner">{{ n }}</div> </div> <div class="panel filled" v-for="n in filledPanels" @click="panelReturn"></div> </section> </article> <article class="text-center" id="timediv" v-show="resultShow"> <h3>あと何分?</h3> <div> 1コマ辺りの作業時間<input class="inputPerPanel" type="number" v-model.number=" perPanel" min="0">分×残り{{ remainedPanelsNumber }}コマ= あと<span class="resultText">{{ remainingTime }}</span>分 ({{ remainingHour }}時間) </div> </article> </div> <!-- ツイートボタン --> <div class="contact text-center"> <a href="https://twitter.com/share" class="twitter-share-button" data-url="https://mitaru.github.io/panels/" data-text="進捗どうですか?あと何コマ?" data-size="large" data-hashtags="あと何コマ"> Tweet </a> </div> </div> <footer class="my-1 pt-5 text-muted text-center text-small"> <ul class="list-inline"> <li class="list-inline-item"> <a href="https://twitter.com/SakaiMitaru"> <i class="fab fa-twitter-square mr-1"></i>SakaiMitaru </a> </li> <li class="list-inline-item"><a href="https://github.com/mitaru/panels.git"> <i class="fab fa-github"></i> </a> </li> </ul> </footer> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js" integrity="sha384-1CmrxMRARb6aLqgBO7yyAxTOQE2AKb9GfXnEo760AUcUmFx3ibVJJAzGytlQcNXd" crossorigin="anonymous"></script> <!-- vue.js --> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="panels.js"></script> </body> </html>panels.csshtml { font-size: 14px; } @media (min-width: 768px) { html { font-size: 16px; } } body { color: rgb(31, 45, 65); background: #F7F7FC; } header { background: #37384E; color: #fff; } .jumbotron { margin: 0; background: #37384E; color: #fff; border-radius: 0 0 40% 40%; } .captionText p { margin-bottom: 50px; } .text-muted { color: #9e9fb4 !important; font-size: 14px; margin: 0; } .main { max-width: 2000px; background: #F7F7FC; } .alertArea { height: 20px; } .alertArea strong { background: rgba(255, 255, 255, 0.6) !important; padding: 2px 5px; border-radius: 4px; color: #37384E; } .panelInput { min-width: 10vw; } .btn-purple { color: #fff; background: #16C995; border: 0; } .btn-purple:hover { background: #0f926d; } .pricing-header { max-width: 1000px; } section { justify-content: center; align-items: center; } input { width: 50px; border: 0; background: #fff; border-radius: 4px; padding: 3px; margin: 2px; color: #766DF4; font-weight: bold; text-align: center; } .resultText { background: rgba(118, 109, 244, 0.08) !important; color: #766df4 !important; font-size: 20px; padding: 0 10px; border-radius: 4px; font-weight: bold; } .panel { width: 150px; height: 100px; background: #fff; border: 5px solid #333; margin: 4px; text-align: center; line-height: 100px; cursor: pointer; user-select: none } .filled { background-color: #fff; background-image: radial-gradient(#16C995 14%, transparent 17%), radial-gradient(#16C995 14%, transparent 17%); background-position: 0 0, 4px 4px; background-size: 8px 8px; } footer { background: #F7F7FC; clear: both; } a .fa-github { font-size: 30px; color: #333; } a .fa-github:hover { opacity: 0.8; } @media screen and (max-width: 480px) { .panel { width: 100px; height: 70px; line-height: 70px; } }panels.js(function () { 'use strict'; new Vue({ el: '#app', data: { remainedPanelsInput: 0, filledPanels: 0, perPanel: 0, resultShow: false, alertShow: false, }, watch: { remainedPanelsInput: { handler: function () { localStorage.setItem('remainedPanelsInput', JSON.stringify(this.remainedPanelsInput)); }, deep: true }, filledPanels: { handler: function () { localStorage.setItem('filledPanels', JSON.stringify(this.filledPanels)); }, deep: true }, perPanel: { handler: function () { localStorage.setItem('perPanel', JSON.stringify(this.perPanel)); }, deep: true }, }, methods: { resultShowFunc: function () { if (this.remainedPanelsInput === 0) { this.alertShow = true; } else { this.resultShow = true; this.alertShow = false; } }, panelFinished: function () { this.filledPanels++; }, panelReturn: function () { this.filledPanels--; }, resultReset: function () { this.remainedPanelsInput = 0; this.filledPanels = 0; this.perPanel = 0; this.resultShow = false; }, }, computed: { remainedPanelsNumber: function () { return this.remainedPanelsInput - this.filledPanels; }, remainingTime: function () { return this.remainedPanelsNumber * this.perPanel; }, remainingHour: function () { return Math.round((this.remainingTime / 60) * 10) / 10; }, }, mounted: function () { this.remainedPanelsInput = JSON.parse(localStorage.getItem('remainedPanelsInput')) || 0; this.filledPanels = JSON.parse(localStorage.getItem('filledPanels')) || 0; this.perPanel = JSON.parse(localStorage.getItem('perPanel')) || 0; if (this.remainedPanelsInput > 0) { this.resultShow = true } }, }) // twitter投稿 !function (d, s, id) { var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https'; if (!d.getElementById(id)) { js = d.createElement(s); js.id = id; js.src = p + '://platform.twitter.com/widgets.js'; fjs.parentNode.insertBefore(js, fjs); } }(document, 'script', 'twitter-wjs'); })();Bootstrap4のこちらの実例をもとに作りました。ほぼ原型残ってないです。
何故わざわざテンプレートをもとに作るかと言うと、レスポンシブ対応が楽だからですね。
全力で先人に頼っていくスタイル。Vue部分
panels.jsnew Vue({ el: '#app', data: { remainedPanelsInput: 0, filledPanels: 0, perPanel: 0, resultShow: false, alertShow: false, }, // 中略 computed: { remainedPanelsNumber: function () { return this.remainedPanelsInput - this.filledPanels; }, remainingTime: function () { return this.remainedPanelsNumber * this.perPanel; }, remainingHour: function () { return Math.round((this.remainingTime / 60) * 10) / 10; }, },
remainedPanelsInput(全○コマの数)から filledPanels(完了したコマ数)を引いて
remainedPanelsNumber(残りコマ数)を出しています。
あと何分?の部分はperPanel(1コマ辺りの作業時間)と
remainedPanelsNumber(残りコマ数)を掛けて算出しています。
また分単位だけでなく時間単位の表記もあった方が親切だと思ったので
remainingHourで計算しました。
Math.round((this.remainingTime / 60) * 10) / 10;
と書くことで、
小数点第二位で切り捨てて表示することができます。panels.html<h3 v-if="!resultShow"> あと<input type="number" v-model.number="remainedPanelsInput" min="0" class="panelInput">コマ? <button type="button" class="ml-2 btn page-link text-light d-inline-block btn-purple" @click="resultShowFunc" v-if="!resultShow">入力完了</button> </h3> <h3 v-else> 全<input type="number" v-model.number="remainedPanelsInput" min="0" class="panelInput">コマ <button type="button" class="ml-2 btn page-link text-light d-inline-block btn-purple" @click="resultReset" v-if="resultShow">リセット</button> </h3>panels.jsmethods: { resultShowFunc: function () { if (this.remainedPanelsInput === 0) { this.alertShow = true; } else { this.resultShow = true; this.alertShow = false; } },こちらはあと何コマ?部分のコードです。
if (this.remainedPanelsInput === 0)
で
あと何コマ?の入力欄が0の場合、入力完了ボタンを押下コマ数を入力してください」とアラートが表示されます。
1以上の数字が入力されている場合は結果が表示されます。
このような表示の分岐にv-show
やv-if
は大変便利です。panels.html<div class="panel" v-for="n in remainedPanelsNumber" @click="panelFinished"> <div class="panelInner">{{ n }}</div> </div> <div class="panel filled" v-for="n in filledPanels" @click="panelReturn"></div>panels.jsmethods: { // 中略 panelFinished: function () { this.filledPanels++; }, panelReturn: function () { this.filledPanels--; },
こちらはコマ部分です。
白いコマはremainedPanelsNumber(残りコマ数)分、
ドットのコマはfilledPanels(完了したコマ数)分表示されます。
v-for="n in remainedPanelsNumber"
と書けば
remainedPanelsNumberの数だけコマを複製してくれます。楽ちんです。
jsで作ろうとしたらコマの中にコマ数を表示するのも大変そうですが、
Vueなら{{ n }}
と書くだけですみます。おお助かる助かる。
またpanelFinished
とpanelReturn
のクリックイベントで
パネルの完了状態を変更しています。panels.jswatch: { remainedPanelsInput: { handler: function () { localStorage.setItem('remainedPanelsInput', JSON.stringify(this.remainedPanelsInput)); }, deep: true }, filledPanels: { handler: function () { localStorage.setItem('filledPanels', JSON.stringify(this.filledPanels)); }, deep: true }, perPanel: { handler: function () { localStorage.setItem('perPanel', JSON.stringify(this.perPanel)); }, deep: true }, }, // 中略 mounted: function () { this.remainedPanelsInput = JSON.parse(localStorage.getItem('remainedPanelsInput')) || 0; this.filledPanels = JSON.parse(localStorage.getItem('filledPanels')) || 0; this.perPanel = JSON.parse(localStorage.getItem('perPanel')) || 0; if (this.remainedPanelsInput > 0) { this.resultShow = true } },ローカルストレージで
remainedPanelsInput
、filledPanels
、
perPanel
の数を保存しています。
監視している変数の変更をトリガーにして勝手に働いてくれる
監視プロパティくんは便利やでホンマ。
途中保存がうまく行かなかったんですが、
mounted
部分をnew Vue
内で一番最後に配置したら
ちゃんと保存されるようになりました。どうして?(無知)デザイン部分
Bootstrap5のとあるテーマを大いに参考にさせていただきました。
見た目もできるだけ可愛くしたかったんです。
panels.css.jumbotron { margin: 0; background: #37384E; color: #fff; border-radius: 0 0 40% 40%; }
border-radius
だけでdivの下部分を丸くできるものなんですね。
今回調べて初めて知りました。他にも色んな表現ができるみたいです。
歪んだ円まで作れるなんてすごい!
参考:今さら聞けない!? CSSのborder-radiusで様々な角丸に挑戦!panels.css.filled { background-color: #fff; background-image: radial-gradient(#16C995 14%, transparent 17%), radial-gradient(#16C995 14%, transparent 17%); background-position: 0 0, 4px 4px; background-size: 8px 8px; }なんと
radial-gradient
を使えば、CSSだけで漫画のトーンみたいなドットの背景が作れます。
CSSでドット柄(水玉模様)を作成 - ホームページのパーツ作成で好きなドットを作ろう!
他にもradial-gradient
でストライプやチェック柄まで作れるみたいです。実際スゴイ!
参考:CSSグラデーションで作った背景パターンのサンプル感想
- 比較的思った通りに作れた
- 見た目もいい感じになったと思う
課題・問題点
- ページ単位の管理機能も作りたかったがややこしすぎてヤコになった
- 今までの知識の延長線上だなと思うのでもっとレベルアップしたい
- BootstrapVue使おうとして挫折した
ここでも全文載せてますが、GitHubでもコードを公開しております。
アドバイスいただけたら嬉しいです。
https://github.com/mitaru/panels次は何を作ろうかなー。ちょっとネタ切れしてきました。
- 投稿日:2020-07-03T13:37:10+09:00
vue-cliでvue createしたらnpm run serveに失敗した。
環境
PC - Windows 10 Home
node.js - 12.16.3
npm - 6.14.4vueプロジェクト作成
まずはグローバルにvue-cliをインストール
> npm install -g @vue/cliインストールを確認
> vue --version @vue/cli 4.4.6適当に作ったディレクトリでプロジェクト作成
今回はvueprojectという名前で進めます。> vue create .いろいろ聞かれますが、とりあえずデフォルトで進めます。
Successfully created project vueproject. Get started with the following commands: $ npm run serveと出れば成功。
言われた通り、サーバーを起動します。> npm run serveエラー発生
npm ERR! code ELIFECYCLE npm ERR! vueproject@0.1.0 serve: `vue-cli-service serve` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the vueproject@0.1.0 serve script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\user\AppData\Roaming\npm-cache\_logs\2020-07-03T03_35_30_681Z-debug.logなるほど。vue-cli-serviceがおかしい?
よく分からん。解決
ググって発見。
https://teratail.com/questions/219324プロジェクトディレクトリから下記を削除する
- node_modules
- package-lock.jsonその後、モジュールを再インストール
> npm install完了したら、サーバー起動
> npm run serveApp running at: - Local: http://localhost:8080/ - Network: http://192.168.2.107:8080/ Note that the development build is not optimized. To create a production build, run npm run build.起動確認。
http://localhost:8080/
にアクセスし、下記の画面が表示されたらOK。
まとめ
別件でも同手順で対処したことあったし、モジュール入れ直しは割とあるあるなのかも。
エラー時にもっといろいろ出る場合は別の原因がありそう。
- 投稿日:2020-07-03T13:14:42+09:00
Vue.js開発環境構築記録
はじめに
私は今までプライベートで開発をした経験がありませんでした。
開発は業務中にやるのみ、家のPCは動画再生専用というおさぼりエンジニアの代表です。というのも、エンジニアに転職したての頃に自宅PCで開発をしようとしたものの
環境構築で躓きただただPCの容量を圧迫した結果に終わる惨めな経験があり
それからというもの自主開発から足が遠退いていました。しかしこの度訳あって自主的にアプリを作成しなければならなくなりました(!)
せっかくなら興味があったVue.jsの開発環境を整えようと孤軍奮闘した記録を残します。環境
macOS High Sierra
先述した環境構築挫折後、MacOS自体の初期化を行っております。
それからChromeとLINEとZoomとSlackしか追加していないとても綺麗な環境です。ちなみに仕事ではWindowsを使っていて、Macでの開発経験ゼロです。
目標
まっさらの状態からvue-cliのインストールを完了する。
やったこと
結果的には以下の作業が必要となりました。
- Homebrewのインストール
- nodebrewのインストール
- Node.jsのインストール
- 環境変数の設定
- vue-cliのインストール
以下、私が試行錯誤した手順を記録します。
Node.jsのインストール
Node.js公式サイトからLTS版をダウンロードしてインストール
Node.jsとnpmがインストールできていることを確認します。$ node -v v12.18.2 $ npm -v 6.14.5Vue.jsのインストール
Vue.js公式サイトを参考に
$ npm install -g @vue/cli npm ERR! code EACCES npm ERR! syscall access npm ERR! path /usr/local/lib/node_modules npm ERR! errno -13 npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules'
ERRORです。。
検索するとこんな記事が:npmでpermission deniedになった時の対処法[mac]対策3:パッケージマネージャ使ってnodeを再インストールする
これが一番簡単そう。やってみる。
Homebrewインストール
Homebrew公式
Mac用パッケージマネージャー。
環境構築について調べていたらたくさんヒットしたけど見て見ぬ振りをしていました。
大人しくインストールしてみます。$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"(インストールに結構時間がかかったので不安でした)
$ brew -v Homebrew 2.4.3 Homebrew/homebrew-core (git revision 5c9580; last commit 2020-07-02)HomebrewでNode.jsをインストール
$ brew install node $ node -v v12.18.2 $ npm -v 6.14.5バージョンは変わらず。これってちゃんとできてるのかな。。
あれちょっと待って、最初にインストールしたやつってアンインストールしたほうがいいんじゃ。。
一旦戻す$ brew uninstall node $ brew list icu4c最初にインストールしたnpm, nodeをアンインストール
こちらを参考に:超簡単にNode.jsのバージョンを管理できるツール「nodebrew」を導入しよう(Mac編)
まずはnpm
$ sudo npm uninstall npm -g $ sudo rm -rf ~/.npm $ npm -v -bash: /usr/local/bin/npm: No such file or directory
次にNode$ lsbom -f -l -s -pf /var/db/receipts/org.nodejs.pkg.bom | while read i; do sudo rm /usr/local/${i}; done can't open /var/db/receipts/org.nodejs.pkg.bom: No such file or directory **** Can't open /var/db/receipts/org.nodejs.pkg.bom.
/var/db/receipts/org.nodejs.pkg.bom
のファイルは存在しているのに。。
全く同じ状況の記事を発見:ローカルからnode.jsを削除する$ which node /usr/local/bin/node $ sudo rm -rf /usr/local/bin/node $ node -v -bash: /usr/local/bin/node: No such file or directory追記
ローカルファイルをよく見たらorg.nodejs.node.pkg.bom
という名前でした。
ファイル名を正しく指定していたらもしかしたら最初の方法でできたかもしれません。確認不足です。
試しにファイル名部分だけ直して再実行してみたら、削除コマンドの結果は全てNo such file or directory
となりました。
どちらでも同じように削除ができたということでしょうか。nodebrewのインストール
$ brew install nodebrew $ nodebrew -v nodebrew 1.0.1 $ brew list icu4c nodebrewnodebrewでNode.jsをインストール
nodebrewでインストールできるバージョンを確認します。
$ nodebrew ls-remote v0.0.1 v0.0.2 v0.0.3 v0.0.4 v0.0.5 v0.0.6 ...(略公式サイトのダウンロードページで推奨されていたv12.18.2をインストールします。
$ nodebrew install-binary v12.18.2 Fetching: https://nodejs.org/dist/v12.18.2/node-v12.18.2-darwin-x64.tar.gz Warning: Failed to create the file Warning: /Users/endouazusa/.nodebrew/src/v12.18.2/node-v12.18.2-darwin-x64.tar. Warning: gz: No such file or directory
またもうまく行きません。
こちらの記事に対処方法が書いてありました。Node.jsをmacOSへインストールする方法
インストール先のディレクトリを手動で作成します。$ mkdir -p ~/.nodebrew/srcもう一度
$ nodebrew install-binary v12.18.2 Fetching: https://nodejs.org/dist/v12.18.2/node-v12.18.2-darwin-x64.tar.gz ######################################################################## 100.0% Installed successfullyインストールされたバージョン一覧を確認
$ nodebrew ls v12.18.2 current: none
current: none
有効になっているバージョンがない状態だそうです。使用するnode.jsのバージョンを指定
$ nodebrew use v12.18.2 use v12.18.2 $ nodebrew ls v12.18.2 current: v12.18.2Pathの設定
まずは設定されている環境変数を表示します。
$ export -p #出力結果一部 declare -x PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"ここにnodeのPATHを追加したいです!
# PATH設定 $ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile # 設定反映 $ source ~/.bash_profile # 環境変数確認 $ export -p declare -x PATH="/Users/xxxxx/.nodebrew/current/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" # nodeのバージョン確認 $ node -v v12.18.2(再)Vue.jsのインストール
$ npm install -g @vue/cliバージョン確認
$ vue -v Usage: vue <command> [options] Options: -V, --version output the version number -h, --help output usage informationおこられる。vは大文字だそうです。
$ vue -V @vue/cli 4.4.6ついでに
Chrome 拡張機能のインストール
公式でオススメされている拡張機能 Vue Devtools を追加します。
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpdさいごに
インストールだけで相当時間がかかってしまいました。難しいです。
プロジェクトの作成はまた別の記事に分けて記録していこうと思います。助けてもらったサイト
- 投稿日:2020-07-03T12:20:15+09:00
Vue Composition API で使えるリアクティブ関連のAPI一覧
Vue Composition APIのリファレンスを見たらリアクティブ周りのAPIが大量に追加されていたのでまとめてみました。
この記事は以下バージョン時点の情報です。
- vuejs/composition-api:
v1.0.0-beta.1
- vuejs/vue-next:
v3.0.0-beta.17
Vue Composition APIのAPI Reference に掲載されているいくつかのAPIは、まだVue2系で使えるComposition APIのプラグイン(vuejs/composition-api)でサポートされていません。
そのAPIについては マークを末尾につけています。サポート対応状況についてはこちらの記載をもとにしています。reactive
引数に渡したオブジェクトのリアクティブなプロキシを返します。
Vue2系のVue.observable()と同等の機能です。ネストされたオブジェクトもリアクティブな値として保持されます。const obj = reactive({ name: "Foo", address: { city: "Bar" } }) obj.address.city = "Huga" console.log(obj.address.city) // Huga型定義
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>;ref
プリミティブな値をrefオブジェクトに変換して返します。refオブジェクトはvalueプロパティでリアクティブな値にアクセスできます。
refの値としてオブジェクトが代入されている場合は、reactiveメソッドによってオブジェクトを深くリアクティブにします。const count = ref(0) console.log(count.value) // 0 count.value++ console.log(count.value) // 1型定義
interface Ref<T = any> { [RefSymbol]: true; value: T; } function ref<T extends object>(value: T): T extends Ref ? T : Ref<UnwrapRef<T>>; function ref<T>(value: T): Ref<UnwrapRef<T>>; function ref<T = any>(): Ref<T | undefined>;computed
コールバック関数を受け取り、戻り値をリアクティブでReadonlyなrefオブジェクトとして返します。
refオブジェクトの値は、コールバック関数内にあるリアクティブな値の変化に応じて再計算されます。
(getterとsetterを持つオブジェクトを渡して書き込み可能なrefオブジェクトを作ることもできます)const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 count.value++ console.log(plusOne.value) // 3 plusOne.value = 4 // Error型定義
// read-only function computed<T>(getter: () => T): Readonly<Ref<Readonly<T>>> // writable function computed<T>(options: { get: () => T set: (value: T) => void }): Ref<T>readonly ️
オブジェクト(ref or reactive or プレーン)を受け取り、読み取り専用のリアクティブなプロキシを返します。
軽量な独自storeの周りの実装で使えそうです。※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const original = reactive({ count: 0 }) const copy = readonly(original) console.log(copy.count) // 0 original.count++ console.log(copy.count) // 1 console.log(isReactive(copy)) // true copy.count++ // Error型定義
function readonly<T extends object>(target: T): Readonly<UnwrapNestedRefs<T>>;customRef
依存関係の追跡と更新のトリガーを明示的に制御するカスタマイズされたrefオブジェクトを返します。
限定された用途でリアクティブな値を使いたい時に良さそうです。※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
// 代入と参照時に必ずconsole.log()を実行するref const useLoggerRef = (value: string) => { return customRef((track, trigger) => ({ get() { console.log(value) track() return value }, set(newValue: string) { console.log(newValue) value = newValue trigger() } })) } const foo = useLoggerRef("") foo.value = "Hoge" // console.log("Hoge")が実行される foo.value = "Bar" // console.log("Bar")が実行される const bar = foo.value // console.log("Bar")が実行される型定義
function customRef<T>(factory: CustomRefFactory<T>): Ref<T> type CustomRefFactory<T> = ( track: () => void, trigger: () => void ) => { get: () => T set: (value: T) => void }markRaw
リアクティブ化されない(プロキシされない)オブジェクトを返します。
適宜markRawを使いプロキシ変換をスキップすることで、不変のデータソースを持つ大規模なリストのレンダリングなどでパフォーマンスを向上させることができます。const foo = markRaw({ baz: "baz" }) const bar = reactive(foo) isReactive(bar) // false reactive関数を実行してもリアクティブにならない型定義
function markRaw<T extends object>(value: T): TshallowReactive
浅いリアクティブなオブジェクトを返します。ネストされたオブジェクトはリアクティブ化されません。
const state = shallowReactive({ foo: 1, nested: { bar: 2 } }) isReactive(state) // true isReactive(state.nested) // false型定義
function shallowReactive<T extends object>(target: T): T;shallowRef
浅いリアクティブなrefオブジェクトを返します。ネストされたオブジェクトはリアクティブ化されません。
const foo = ref({}) const bar = shallowRef({}) isReactive(foo.value) // true isReactive(bar.value) // false型定義
function shallowRef<T>(value: T): T extends Ref ? T : Ref<T>;shallowReadonly
浅いreadonlyなオブジェクトを返します。ネストされたオブジェクトはreadonlyになりません。
※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const state = shallowReadonly({ foo: 1, nested: { bar: 2 } }) // mutating state's own properties will fail state.foo++ // ...but works on nested objects isReadonly(state.nested) // false state.nested.bar++ // works型定義
function shallowReadonly<T extends object>(target: T): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]>; }>;toRaw
プロキシされたリアクティブなオブジェクトの生データを返します。
const foo = {} const reactiveFoo = reactive(foo) isReactive(toRaw(reactiveFoo)) // false toRaw(reactiveFoo) === foo // true型定義
function toRaw<T>(observed: T): T;unref
引数がrefの場合は
ref.value
の値を、それ以外だったら引数自体を返します。
val = isRef(val) ? val.value : val
の糖衣構文です。const foo = ref(1) const bar = 1 unref(foo) // 1 unref(bar) // 1型定義
function unref<T>(ref: T): T extends Ref<infer V> ? V : T;toRef
reactiveなオブジェクトの指定のプロパティをrefオブジェクトに変換して返します。
propsの値をリアクティブを切らず、setup関数外のコンポーザブルな関数に渡すときなどに使えます。const state = reactive({ foo: 1, bar: 2 }) const fooRef = toRef(state, 'foo') isRef(fooRef) // true型定義
function toRef<T extends object, K extends keyof T>(object: T, key: K): Ref<T[K]>;toRefs
reactiveなオブジェクトの全てのプロパティをrefオブジェクトに変換したオブジェクトを返します。
const state = reactive({ foo: 1, bar: 2 }) const stateAsRefs = toRefs(state) isRef(stateAsRefs.foo) // true isRef(stateAsRefs.bar) // true型定義
type ToRefs<T = any> = { [K in keyof T]: Ref<T[K]>; }; function toRefs<T extends object>(object: T): ToRefs<T>;isRef
refオブジェクトでラップされているかどうかを判定します。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); isRef(foo) // true isRef(bar) // false型定義
isRef<T>(value: any): value is Ref<T>;isReactive
reactiveかどうか判定します。reactiveで作られたオブジェクトをラップしたreadonlyなオブジェクトの場合にもtrueを返します。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); const baz = readonly(bar); const hoge = readonly(foo) isReactive(foo) // false isReactive(bar) // true isReactive(baz) // true isReactive(hoge) // false型定義
function isReactive(value: unknown): boolean;isReadonly
オブジェクトがreadonlyかどうか判定します。
※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); const baz = readonly(bar); isReadonly(foo) // false isReadonly(bar) // false isReadonly(baz) // true型定義
function isReadonly(value: unknown): boolean;isProxy
オブジェクトがProxyか判定します。reactiveかreadonlyで作成したオブジェクトが該当します。
※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); const baz = readonly(bar); isProxy(foo) // false isProxy(bar) // true isProxy(baz) // true型定義
function isProxy(value: unknown): boolean;終わりに
以上「Vue Composition APIのリアクティブ関連の関数一覧」でした。思った以上にAPIが多いですね。
業務ではref, reactive, toRef, toRefs, isRefくらいしか使ってなかったです。
今後はどんどん活用していきたいです。参考
- 投稿日:2020-07-03T12:20:15+09:00
Vue Composition API リアクティブ周りの関数一覧
Vue Composition APIのリファレンスを見たらリアクティブ周りのAPIが大量に追加されていたのでまとめてみました。
この記事は以下バージョン時点の情報です。
- vuejs/composition-api:
v1.0.0-beta.1
- vuejs/vue-next:
v3.0.0-beta.17
Vue Composition APIのAPI Reference に掲載されているいくつかのAPIは、まだVue2系で使えるComposition APIのプラグイン(vuejs/composition-api)でサポートされていません。
そのAPIについては マークを末尾につけています。サポート対応状況についてはこちらの記載をもとにしています。reactive
引数に渡したオブジェクトのリアクティブなプロキシを返します。
Vue2系のVue.observable()と同等の機能です。ネストされたオブジェクトもリアクティブな値として保持されます。const obj = reactive({ name: "Foo", address: { city: "Bar" } }) obj.address.city = "Huga" console.log(obj.address.city) // Huga型定義
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>;ref
プリミティブな値をrefオブジェクトに変換して返します。refオブジェクトはvalueプロパティでリアクティブな値にアクセスできます。
refの値としてオブジェクトが代入されている場合は、reactiveメソッドによってオブジェクトを深くリアクティブにします。const count = ref(0) console.log(count.value) // 0 count.value++ console.log(count.value) // 1型定義
interface Ref<T = any> { [RefSymbol]: true; value: T; } function ref<T extends object>(value: T): T extends Ref ? T : Ref<UnwrapRef<T>>; function ref<T>(value: T): Ref<UnwrapRef<T>>; function ref<T = any>(): Ref<T | undefined>;computed
コールバック関数を受け取り、戻り値をリアクティブでReadonlyなrefオブジェクトとして返します。
refオブジェクトの値は、コールバック関数内にあるリアクティブな値の変化に応じて再計算されます。
(getterとsetterを持つオブジェクトを渡して書き込み可能なrefオブジェクトを作ることもできます)const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 count.value++ console.log(plusOne.value) // 3 plusOne.value = 4 // Error型定義
// read-only function computed<T>(getter: () => T): Readonly<Ref<Readonly<T>>> // writable function computed<T>(options: { get: () => T set: (value: T) => void }): Ref<T>readonly ️
オブジェクト(ref or reactive or プレーン)を受け取り、読み取り専用のリアクティブなプロキシを返します。
軽量な独自storeの周りの実装で使えそうです。※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const original = reactive({ count: 0 }) const copy = readonly(original) console.log(copy.count) // 0 original.count++ console.log(copy.count) // 1 console.log(isReactive(copy)) // true copy.count++ // Error型定義
function readonly<T extends object>(target: T): Readonly<UnwrapNestedRefs<T>>;customRef
依存関係の追跡と更新のトリガーを明示的に制御するカスタマイズされたrefオブジェクトを返します。
限定された用途でリアクティブな値を使いたい時に良さそうです。※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
// 代入と参照時に必ずconsole.log()を実行するref const useLoggerRef = (value: string) => { return customRef((track, trigger) => ({ get() { console.log(value) track() return value }, set(newValue: string) { console.log(newValue) value = newValue trigger() } })) } const foo = useLoggerRef("") foo.value = "Hoge" // console.log("Hoge")が実行される foo.value = "Bar" // console.log("Bar")が実行される const bar = foo.value // console.log("Bar")が実行される型定義
function customRef<T>(factory: CustomRefFactory<T>): Ref<T> type CustomRefFactory<T> = ( track: () => void, trigger: () => void ) => { get: () => T set: (value: T) => void }markRaw
リアクティブ化されない(プロキシされない)オブジェクトを返します。
適宜markRawを使いプロキシ変換をスキップすることで、不変のデータソースを持つ大規模なリストのレンダリングなどでパフォーマンスを向上させることができます。const foo = markRaw({ baz: "baz" }) const bar = reactive(foo) isReactive(bar) // false reactive関数を実行してもリアクティブにならない型定義
function markRaw<T extends object>(value: T): TshallowReactive
浅いリアクティブなオブジェクトを返します。ネストされたオブジェクトはリアクティブ化されません。
const state = shallowReactive({ foo: 1, nested: { bar: 2 } }) isReactive(state) // true isReactive(state.nested) // false型定義
function shallowReactive<T extends object>(target: T): T;shallowRef
浅いリアクティブなrefオブジェクトを返します。ネストされたオブジェクトはリアクティブ化されません。
const foo = ref({}) const bar = shallowRef({}) isReactive(foo.value) // true isReactive(bar.value) // false型定義
function shallowRef<T>(value: T): T extends Ref ? T : Ref<T>;shallowReadonly
浅いreadonlyなオブジェクトを返します。ネストされたオブジェクトはreadonlyになりません。
※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const state = shallowReadonly({ foo: 1, nested: { bar: 2 } }) // mutating state's own properties will fail state.foo++ // ...but works on nested objects isReadonly(state.nested) // false state.nested.bar++ // works型定義
function shallowReadonly<T extends object>(target: T): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]>; }>;toRaw
プロキシされたリアクティブなオブジェクトの生データを返します。
const foo = {} const reactiveFoo = reactive(foo) isReactive(toRaw(reactiveFoo)) // false toRaw(reactiveFoo) === foo // true型定義
function toRaw<T>(observed: T): T;unref
引数がrefの場合は
ref.value
の値を、それ以外だったら引数自体を返します。
val = isRef(val) ? val.value : val
の糖衣構文です。const foo = ref(1) const bar = 1 unref(foo) // 1 unref(bar) // 1型定義
function unref<T>(ref: T): T extends Ref<infer V> ? V : T;toRef
reactiveなオブジェクトの指定のプロパティをrefオブジェクトに変換して返します。
propsの値をリアクティブを切らず、setup関数外のコンポーザブルな関数に渡すときなどに使えます。const state = reactive({ foo: 1, bar: 2 }) const fooRef = toRef(state, 'foo') isRef(fooRef) // true型定義
function toRef<T extends object, K extends keyof T>(object: T, key: K): Ref<T[K]>;toRefs
reactiveなオブジェクトの全てのプロパティをrefオブジェクトに変換したオブジェクトを返します。
const state = reactive({ foo: 1, bar: 2 }) const stateAsRefs = toRefs(state) isRef(stateAsRefs.foo) // true isRef(stateAsRefs.bar) // true型定義
type ToRefs<T = any> = { [K in keyof T]: Ref<T[K]>; }; function toRefs<T extends object>(object: T): ToRefs<T>;isRef
refオブジェクトでラップされているかどうかを判定します。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); isRef(foo) // true isRef(bar) // false型定義
isRef<T>(value: any): value is Ref<T>;isReactive
reactiveかどうか判定します。reactiveで作られたオブジェクトをラップしたreadonlyなオブジェクトの場合にもtrueを返します。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); const baz = readonly(bar); const hoge = readonly(foo) isReactive(foo) // false isReactive(bar) // true isReactive(baz) // true isReactive(hoge) // false型定義
function isReactive(value: unknown): boolean;isReadonly
オブジェクトがreadonlyかどうか判定します。
※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); const baz = readonly(bar); isReadonly(foo) // false isReadonly(bar) // false isReadonly(baz) // true型定義
function isReadonly(value: unknown): boolean;isProxy
オブジェクトがProxyか判定します。reactiveかreadonlyで作成したオブジェクトが該当します。
※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); const baz = readonly(bar); isProxy(foo) // false isProxy(bar) // true isProxy(baz) // true型定義
function isProxy(value: unknown): boolean;終わりに
以上「Vue Composition APIのリアクティブ関連の関数一覧」でした。思った以上にAPIが多いですね。
業務ではref, reactive, toRef, toRefs, isRefくらいしか使ってなかったです。
今後はどんどん活用していきたいです。参考
- 投稿日:2020-07-03T12:20:15+09:00
Vue Composition API v1-beta で使えるリアクティブ関連のAPI一覧
Vue Composition APIのリファレンスを見たらリアクティブ周りのAPIが大量に追加されていたのでまとめてみました。
この記事は以下バージョン時点の情報です。
- vuejs/composition-api:
v1.0.0-beta.1
- vuejs/vue-next:
v3.0.0-beta.17
Vue Composition APIのAPI Reference に掲載されているいくつかのAPIは、まだVue2系で使えるComposition APIのプラグイン(vuejs/composition-api)でサポートされていません。
そのAPIについては マークを末尾につけています。サポート対応状況についてはこちらの記載をもとにしています。reactive
引数に渡したオブジェクトのリアクティブなプロキシを返します。
Vue2系のVue.observable()と同等の機能です。ネストされたオブジェクトもリアクティブな値として保持されます。const obj = reactive({ name: "Foo", address: { city: "Bar" } }) obj.address.city = "Huga" console.log(obj.address.city) // Huga型定義
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>;ref
プリミティブな値をrefオブジェクトに変換して返します。refオブジェクトはvalueプロパティでリアクティブな値にアクセスできます。
refの値としてオブジェクトが代入されている場合は、reactiveメソッドによってオブジェクトを深くリアクティブにします。const count = ref(0) console.log(count.value) // 0 count.value++ console.log(count.value) // 1型定義
interface Ref<T = any> { [RefSymbol]: true; value: T; } function ref<T extends object>(value: T): T extends Ref ? T : Ref<UnwrapRef<T>>; function ref<T>(value: T): Ref<UnwrapRef<T>>; function ref<T = any>(): Ref<T | undefined>;computed
コールバック関数を受け取り、戻り値をリアクティブでReadonlyなrefオブジェクトとして返します。
refオブジェクトの値は、コールバック関数内にあるリアクティブな値の変化に応じて再計算されます。
(getterとsetterを持つオブジェクトを渡して書き込み可能なrefオブジェクトを作ることもできます)const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 count.value++ console.log(plusOne.value) // 3 plusOne.value = 4 // Error型定義
// read-only function computed<T>(getter: () => T): Readonly<Ref<Readonly<T>>> // writable function computed<T>(options: { get: () => T set: (value: T) => void }): Ref<T>readonly ️
オブジェクト(ref or reactive or プレーン)を受け取り、読み取り専用のリアクティブなプロキシを返します。
軽量な独自storeの周りの実装で使えそうです。※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const original = reactive({ count: 0 }) const copy = readonly(original) console.log(copy.count) // 0 original.count++ console.log(copy.count) // 1 console.log(isReactive(copy)) // true copy.count++ // Error型定義
function readonly<T extends object>(target: T): Readonly<UnwrapNestedRefs<T>>;customRef
依存関係の追跡と更新のトリガーを明示的に制御するカスタマイズされたrefオブジェクトを返します。
限定された用途でリアクティブな値を使いたい時に良さそうです。※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
// 代入と参照時に必ずconsole.log()を実行するref const useLoggerRef = (value: string) => { return customRef((track, trigger) => ({ get() { console.log(value) track() return value }, set(newValue: string) { console.log(newValue) value = newValue trigger() } })) } const foo = useLoggerRef("") foo.value = "Hoge" // console.log("Hoge")が実行される foo.value = "Bar" // console.log("Bar")が実行される const bar = foo.value // console.log("Bar")が実行される型定義
function customRef<T>(factory: CustomRefFactory<T>): Ref<T> type CustomRefFactory<T> = ( track: () => void, trigger: () => void ) => { get: () => T set: (value: T) => void }markRaw
リアクティブ化されない(プロキシされない)オブジェクトを返します。
適宜markRawを使いプロキシ変換をスキップすることで、不変のデータソースを持つ大規模なリストのレンダリングなどでパフォーマンスを向上させることができます。const foo = markRaw({ baz: "baz" }) const bar = reactive(foo) isReactive(bar) // false reactive関数を実行してもリアクティブにならない型定義
function markRaw<T extends object>(value: T): TshallowReactive
浅いリアクティブなオブジェクトを返します。ネストされたオブジェクトはリアクティブ化されません。
const state = shallowReactive({ foo: 1, nested: { bar: 2 } }) isReactive(state) // true isReactive(state.nested) // false型定義
function shallowReactive<T extends object>(target: T): T;shallowRef
浅いリアクティブなrefオブジェクトを返します。ネストされたオブジェクトはリアクティブ化されません。
const foo = ref({}) const bar = shallowRef({}) isReactive(foo.value) // true isReactive(bar.value) // false型定義
function shallowRef<T>(value: T): T extends Ref ? T : Ref<T>;shallowReadonly
浅いreadonlyなオブジェクトを返します。ネストされたオブジェクトはreadonlyになりません。
※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const state = shallowReadonly({ foo: 1, nested: { bar: 2 } }) // mutating state's own properties will fail state.foo++ // ...but works on nested objects isReadonly(state.nested) // false state.nested.bar++ // works型定義
function shallowReadonly<T extends object>(target: T): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]>; }>;toRaw
プロキシされたリアクティブなオブジェクトの生データを返します。
const foo = {} const reactiveFoo = reactive(foo) isReactive(toRaw(reactiveFoo)) // false toRaw(reactiveFoo) === foo // true型定義
function toRaw<T>(observed: T): T;unref
引数がrefの場合は
ref.value
の値を、それ以外だったら引数自体を返します。
val = isRef(val) ? val.value : val
の糖衣構文です。const foo = ref(1) const bar = 1 unref(foo) // 1 unref(bar) // 1型定義
function unref<T>(ref: T): T extends Ref<infer V> ? V : T;toRef
reactiveなオブジェクトの指定のプロパティをrefオブジェクトに変換して返します。
propsの値をリアクティブを切らず、setup関数外のコンポーザブルな関数に渡すときなどに使えます。const state = reactive({ foo: 1, bar: 2 }) const fooRef = toRef(state, 'foo') isRef(fooRef) // true型定義
function toRef<T extends object, K extends keyof T>(object: T, key: K): Ref<T[K]>;toRefs
reactiveなオブジェクトの全てのプロパティをrefオブジェクトに変換したオブジェクトを返します。
const state = reactive({ foo: 1, bar: 2 }) const stateAsRefs = toRefs(state) isRef(stateAsRefs.foo) // true isRef(stateAsRefs.bar) // true型定義
type ToRefs<T = any> = { [K in keyof T]: Ref<T[K]>; }; function toRefs<T extends object>(object: T): ToRefs<T>;isRef
refオブジェクトでラップされているかどうかを判定します。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); isRef(foo) // true isRef(bar) // false型定義
isRef<T>(value: any): value is Ref<T>;isReactive
reactiveかどうか判定します。reactiveで作られたオブジェクトをラップしたreadonlyなオブジェクトの場合にもtrueを返します。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); const baz = readonly(bar); const hoge = readonly(foo) isReactive(foo) // false isReactive(bar) // true isReactive(baz) // true isReactive(hoge) // false型定義
function isReactive(value: unknown): boolean;isReadonly
オブジェクトがreadonlyかどうか判定します。
※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); const baz = readonly(bar); isReadonly(foo) // false isReadonly(bar) // false isReadonly(baz) // true型定義
function isReadonly(value: unknown): boolean;isProxy
オブジェクトがProxyか判定します。reactiveかreadonlyで作成したオブジェクトが該当します。
※ 2020/07/03現在、vue-composition-apiプラグインではサポートされてません。
const foo = ref("foo"); const bar = reactive({ bar: "bar" }); const baz = readonly(bar); isProxy(foo) // false isProxy(bar) // true isProxy(baz) // true型定義
function isProxy(value: unknown): boolean;終わりに
以上「Vue Composition APIのリアクティブ関連の関数一覧」でした。思った以上にAPIが多いですね。
業務ではref, reactive, toRef, toRefs, isRefくらいしか使ってなかったです。
今後はどんどん活用していきたいです。参考
- 投稿日:2020-07-03T06:42:41+09:00
条件付きレンダリングって?
条件付きレンダリングてなだろう?
プログラミングにおいて複数の反応を用意して、一定の入力によって様々な出力へと変化するいわば制御構文というものがあります。これは一般的には
if文
を用いることで可能になるんですが私が今勉強しているVue.jsで条件付きレンダリングにおいてはv-if
やv-else
というものを使って表現するらしい!
まぁ簡潔に言えば条件付きレンダリングとは制御構文であると言えますね!百聞は一見に如かず!
とりあえず何かしらこの
v-if
とやらを使ってみるのが一番わかりやすいと思うのでサンプルプログラムを作ってみましょう。sample.html<div id="app"> <p v-if="flag" class="ok"> This is correct display! </p> <p v-else class="ng"> uncorrect display!!!!!!! </p> </div> <button onClick="doAction();"> Click </button> <script> var data = { flag: true } var app = new Vue({ el: '#app', data: data }); function doAction() { data.flag = !data.flag; } </script>こんな感じのことを書いといて、webで開くとこんな感じです...
サーセン!背景のこと全く考えてなくて画像の境界線が分からなくなってるけど気にしないでくれると助かります
プログラム自体は至って簡単なものです!
flag
に格納されている値がtrue
であればv-if
の方の表示がされます。ボタンを押すとdoAction
のメソッドが動き、!data.flag
によりdata
のなかのflag
の値の否定、つまりfalse
へ変化しdata.flag
へ格納します。したがって今度は値がfalseのためv-else
の方の表示がされます。
書いて見ればなんてことないただのif文でしたな〜言葉に騙されないで!
参考書とかで勉強してると時々「は⁉︎何これ!初めて聞いたはこの機能!」ということが多々あると思います。でも実際に書いて実行してみると意外と今回のようになんてことない初歩中の初歩だったりする。なんか難しい言葉が出てきたな〜とか思ったらとにかくプログラムを実際に書いてみる!これに限ることを学んだ今日この頃...
この記事ほとんど書く必要なかった〜〜〜
- 投稿日:2020-07-03T00:27:33+09:00
Vueでビルドしても画面が真っ白(Uncaught RangeError: Duplicate use of selection JSON ID cell
事象
npm run serve
を実行して最後までビルドはされるが画面が真っ白になる。Uncaught RangeError: Duplicate use of selection JSON ID cell解決方法
エラー文をそのままググって検索して出てきたissueを参考。
node_moduleを削除して再度npm installすることで解決。https://github.com/scrumpy/tiptap/issues/316
原因は正直よくわかってない。