20200703のvue.jsに関する記事は14件です。

なぜ、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/183

Vue3の目玉の機能として、「Composition API」があります。

この記事では、「”なぜ” “なんのために” Composition APIを使うのか」理由について、まとめています。

「Composition APIの使い方」についての説明は、この記事では割愛します。

Composition APIを使う理由、なんもわからん

dog3_1_question.png

これまで、私自身、Composition APIを使う理由が、よくわかっていませんでした。

なんとなく、「setup関数でまとめて処理を登録しておく」くらいのことを雰囲気で理解していました。

「Composition APIで書くほうがイケてるし、カッコいい(重要)」くらいのノリ

何のために使うのか、メリットがよく分からない状態でした。

62783026-810e6180-ba89-11e9-8774-e7771c8095d6.png

Composition APIについて調べていると、↑の図、よく見ませんか?

既存APIとComposition APIのコードを比較して、メリットを提示しているようですけど

私は

そんなに違わないように見える」

違いがよくわからん

っといった感想を持ちました。

私と同じように

「Vueに新しく、Composition APIってのが出るらしいけど、なんとなく使おうかな〜」

「でも、なんのために使うんだっけ

と思っている方が多いのではないかなと考え、この記事を執筆しています。

この記事が、誰かのお役に立てれば幸いです。

Composition API RFC を読んだら、「なぜ、使うのか」を理解した

先日 @shwld が主催した

「Vue Composition APIを知る会」

で、Composition APIのRFCを読む機会があり、そこで改めて「”なぜ” Composition APIを使うのか」理解することができました。

IMG_0559.jpg

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 型強化」などが理由として挙げられますが、この記事では割愛します)

daiku_nokogiri_kiru.png

ロジックの抽出と再利用

「ロジックの抽出と再利用」は、なぜするのか。

「ロジックの抽出と再利用」が必要な理由は、
複雑に肥大化したコンポーネントを、小分けにして関心事で分別し、クリーンな状態に整理するためです。

近年、Vue を使用して大規模なプロジェクトを構築することが増えました。

肥大化してメンテしにくいコンポーネントを目の当たりにして、我々エンジニアが苦しむことも増えました。

character_program_fat.png

肥大化したコンポーネントのコードは、依存関係が複雑で量も多くコードを読むこと自体難しくなります。

これまでのVue2.xでは、「複数のコンポーネント間でロジックを抽出して再利用するためのクリーンでコストのかからないメカニズム」が欠如していました。

これを解決するのが、Composition APIです。

Composition APIによって、コンポーネントのコードを整理する際に、開発者により高い柔軟性を提供します。
コンポーネント間で、ロジックを抽出して再利用することが、簡単になります。

game_ken_seiken.png

つまり、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」は使わないようにしましょう。

nezumi_tori.png

なぜmixinがアンチパターンなのかについては、下記の記事に詳しく書かれてあります。

俺がやらかしたVue mixinのアンチパターンから学ぶmixinの使い方と代替案
https://aloerina01.github.io/blog/2018-12-25-1

Composition APIを使う際のデメリット

Composition APIを使うメリットは、理解できたかと思います。

じゃあ、デメリットはないのか?

あります。

デメリットを一言でいうと、「自由すぎ」です。

Composition APIによって、自由にロジックを切り出せるようになりますが、一方で、いままでVueが暗黙的に行ってくれていたレールから外れることを意味します。

どういう単位でロジックを切り出したり、共通化したり、といった疑問に対して、明確な正解がないのです。

つまり、Vueが管理している壁の中の世界から、壁の外の無秩序な荒野へと放り出されるのです。

無秩序な荒野で、テキトーに開発していると、隠れていた猛獣に襲われたり、食料が尽きたりといった致命的なアクシデントに見舞われます。計画的に外の世界を開拓しないと危険がいっぱいなのです。

「設計」や「アーキテクチャ」が重要になる

その荒野に秩序を与えるのは、我々、調査兵団(エンジニア)の「設計」です。

map_takara_chizu.png

今後は、「アーキテクチャ」が重要になります。

つまり、デメリットは、ちゃんと「設計」や「アーキテクチャ」を考えなければならないことです。

例えば、「クリーンアーキテクチャ」を理解して、実装していくことが求められます。

実装クリーンアーキテクチャ
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.com

API Reference
https://composition-api.vuejs.org/api.html

Composition API Demo
https://github.com/LinusBorg/composition-api-demos

vue-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/f055679e9974dbc3f977

Vue Composition API v1-beta で使えるリアクティブ関連のAPI一覧
https://qiita.com/ryo2132/items/6dc51ede8082dea75812

Vue Composition API を使ったストアパターンと TypeScript の組み合わせはどのくらいスケールするか?
https://qiita.com/tmy/items/a545e44100247c364a71

Composition API 勘所など
https://webneko.dev/posts/notices-of-composition-api-in-vue3-eve

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

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.1

active_storageに保存するまでは
https://qiita.com/ozin/items/5ec81a4b126b8ebf7a96
この記事でやりました。

このあとvue側に表示するまでを書きます。Vueでは単一ファイルコンポーネントで実装しています。
ライブラリのインストール方法は省略します。

モデル

diary.rb
has_one_attached :image

# 省略

コントローラ

diaries_controller.rb
  def 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
  end

axiosでリクエストを送る

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>

これで表示できました。
指摘などありましたらお願いします!

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

【Vueで使える】差がつくCSS画像アニメーションまとめ49選

スクリーンショット 2020-07-02 0.24.52.png

こちらの記事に記載のデザイン・コードは全て自由に使っていただいて大丈夫です(筆者が作成したため)
プロジェクトに取り込んでより充実したデザインにしてもらえれば○
*動きだけ確認したい初学者の方はJSFiddle使ってみるといいですよ


1. hoverで画像が拡大(ズーム)するCSSアニメーション3選(+filterエフェクト)

hover-animation-zoom-image-top.png

2. box-shadowとhoverで3D画像に動きをつけるCSSアニメーション3選!

hover-3d-image-animation.png

3. hoverとtransitionでオシャレなCSS画像エフェクト4選!(背景がスっと表示される)

hover-animation-image-onbackground.png

4. CSSだけ!hoverとtransitionで作る動的画像エフェクト4選(コピペOK)

hover-4effect-image.png

5. hoverとfilter:grayscaleで白黒に切り替え!CSS画像エフェクト3選

hover-animation-filter-grayscale.png

6. hoverとtransformで画像が回転するCSSアニメーション3選【3分で作れる!】

hover-animation-spin.png

7. hoverで画像がスライド!margin-leftとscaleでCSSアニメーション3選

[hover-animation-slides.png

](https://www.twinzlabo.com/css-animation-hover-slide-transition/)

8. hoverとfilter:brightnessで明るさ調節!CSS画像エフェクト3選

filter-brightness-topimage.png

9. overflow: hiddenで美しい!CSSアニメーション3選(画像一覧)

hover-animation-overflow-top.png

10. 【CSSだけ】filterとopacityで洗練されたスライダーアニメーション3選

click-animation-slider-top.png

11. hoverで画像が鏡みたいに反射!position:absoluteとfilter多用でCSSアニメーション3選

css-effects-hover-filter-absolute.png

12. hoverで画像の色が暴れ出す!filter: hue-rotateとtransitionでCSSエフェクト3選

[css-effects-hover-filter-hue-rotate.png

](https://www.twinzlabo.com/css-effects-hover-filter-hue-rotate/)

13. transitionとfilterで美しく変化するCSS画像エフェクト5選(基礎から応用まで)

hover-animation-filters-transition.png

14. hoverでぼかし画像が動き出す!filter:blurとtransitionでCSSエフェクト3選

css-cool-effects-hover-filter-1.png

15. 【filterプロパティ】hoverすると美しさ100倍!CSS画像エフェクト3選

hover-effects-filters-mix.png


いかがでしたでしょうか?
お役に立てたら嬉しいです。

最後に注意書きとして
コメント欄に心ないコメントを投稿する方がまれにいますが迷惑なので問答無用で速攻ブロックします。

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

`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 を実行すると無事に成功しました。

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

【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

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

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

質問が終わるとプロジェクトの作成が始まります。
終了すると、実行ディレクトリ/プロジェクト名配下にファイルが作成されました。
スクリーンショット 2020-07-03 16.06.16.png

cd my-project

cd my-project

npm 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 details

npm run dev

$ npm run dev

 DONE  Compiled successfully in 6781ms                                                                                              16:13:48

 I  Your application is running here: http://localhost:8080

できました:thumbsup:
スクリーンショット 2020-07-03 16.14.32.png

停止する場合は Control + c

Visual Studio Code で開いてみる

Welcome pageのOpen folderから先ほどinitしたフォルダを選択します。
スクリーンショット 2020-07-03 16.47.36.png

さいごに

開発を始める準備がようやく整いました。
ここから実際にソースを触るのはまた別の記事に残します。

助けてもらったサイト

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

【Vue】漫画の進捗管理ツール作ってみた

01.png
02.png
03.png

あと何コマ?
コマ数で漫画の進捗管理するツールを作りました。
コマ数を入力するとコマが出てきて、
終わったコマをクリックすると塗りつぶされて完了状態になります。
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.css
html {
  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のこちらの実例をもとに作りました。ほぼ原型残ってないです。
何故わざわざテンプレートをもとに作るかと言うと、レスポンシブ対応が楽だからですね。
10.png
全力で先人に頼っていくスタイル。

Vue部分

panels.js
  new 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;
      },
    },

02.png
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.js
methods: {
  resultShowFunc: function () {
    if (this.remainedPanelsInput === 0) {
      this.alertShow = true;
    } else {
      this.resultShow = true;
      this.alertShow = false;
    }
  },

こちらはあと何コマ?部分のコードです。
image.png
image.png
if (this.remainedPanelsInput === 0)
あと何コマ?の入力欄が0の場合、入力完了ボタンを押下コマ数を入力してください」とアラートが表示されます。
image.png
1以上の数字が入力されている場合は結果が表示されます。
このような表示の分岐にv-showv-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.js
methods: {
  // 中略
  panelFinished: function () {
    this.filledPanels++;
  },
  panelReturn: function () {
    this.filledPanels--;
  },

image.png
image.png
こちらはコマ部分です。
白いコマはremainedPanelsNumber(残りコマ数)分、
ドットのコマはfilledPanels(完了したコマ数)分表示されます。
v-for="n in remainedPanelsNumber"と書けば
remainedPanelsNumberの数だけコマを複製してくれます。楽ちんです。
jsで作ろうとしたらコマの中にコマ数を表示するのも大変そうですが、
Vueなら{{ n }}と書くだけですみます。おお助かる助かる。
またpanelFinishedpanelReturnのクリックイベントで
パネルの完了状態を変更しています。

panels.js
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
  },
},
// 中略
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
  }
},

ローカルストレージでremainedPanelsInputfilledPanels
perPanelの数を保存しています。
監視している変数の変更をトリガーにして勝手に働いてくれる
監視プロパティくんは便利やでホンマ。
途中保存がうまく行かなかったんですが、
mounted部分をnew Vue内で一番最後に配置したら
ちゃんと保存されるようになりました。どうして?(無知)

デザイン部分

Bootstrap5のとあるテーマを大いに参考にさせていただきました。
見た目もできるだけ可愛くしたかったんです。
image.png

panels.css
.jumbotron {
  margin: 0;
  background: #37384E;
  color: #fff;
  border-radius: 0 0 40% 40%;
}

border-radiusだけでdivの下部分を丸くできるものなんですね。
今回調べて初めて知りました。他にも色んな表現ができるみたいです。
歪んだ円まで作れるなんてすごい!
参考:今さら聞けない!? CSSのborder-radiusで様々な角丸に挑戦!

image.png

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

次は何を作ろうかなー。ちょっとネタ切れしてきました。

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

vue-cliでvue createしたらnpm run serveに失敗した。

環境

PC - Windows 10 Home
node.js - 12.16.3
npm - 6.14.4

vueプロジェクト作成

まずはグローバルに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 serve
  App 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。
FireShot Capture 002 - portfolio - localhost.png

まとめ

別件でも同手順で対処したことあったし、モジュール入れ直しは割とあるあるなのかも。
エラー時にもっといろいろ出る場合は別の原因がありそう。

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

Vue.js開発環境構築記録

はじめに

私は今までプライベートで開発をした経験がありませんでした。
開発は業務中にやるのみ、家のPCは動画再生専用というおさぼりエンジニアの代表です。

というのも、エンジニアに転職したての頃に自宅PCで開発をしようとしたものの
環境構築で躓きただただPCの容量を圧迫した結果に終わる惨めな経験があり
それからというもの自主開発から足が遠退いていました。

しかしこの度訳あって自主的にアプリを作成しなければならなくなりました(!)
せっかくなら興味があったVue.jsの開発環境を整えようと孤軍奮闘した記録を残します。

環境

macOS High Sierra

先述した環境構築挫折後、MacOS自体の初期化を行っております。
それからChromeとLINEとZoomとSlackしか追加していないとても綺麗な環境です。

ちなみに仕事ではWindowsを使っていて、Macでの開発経験ゼロです。

目標

まっさらの状態からvue-cliのインストールを完了する。

やったこと

結果的には以下の作業が必要となりました。

  1. Homebrewのインストール
  2. nodebrewのインストール
  3. Node.jsのインストール
  4. 環境変数の設定
  5. vue-cliのインストール

以下、私が試行錯誤した手順を記録します。

Node.jsのインストール

Node.js公式サイトからLTS版をダウンロードしてインストール
Node.jsとnpmがインストールできていることを確認します。

$ node -v
v12.18.2

$ npm -v
6.14.5

Vue.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'

:thumbsdown:
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)

:thumbsup:

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

:thumbsup:
次に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

:thumbsup:

追記
ローカルファイルをよく見たらorg.nodejs.node.pkg.bomという名前でした。
ファイル名を正しく指定していたらもしかしたら最初の方法でできたかもしれません。確認不足です。
試しにファイル名部分だけ直して再実行してみたら、削除コマンドの結果は全てNo such file or directoryとなりました。
どちらでも同じように削除ができたということでしょうか。

nodebrewのインストール

$ brew install nodebrew

$ nodebrew -v
nodebrew 1.0.1

$ brew list
icu4c       nodebrew

nodebrewで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

:thumbsdown:
またもうまく行きません。
こちらの記事に対処方法が書いてありました。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

:thumbsup:

インストールされたバージョン一覧を確認

$ 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.2

:thumbsup:

Pathの設定

まずは設定されている環境変数を表示します。

$ 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

:thumbsup:

(再)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

:thumbsup::thumbsup::thumbsup::thumbsup::thumbsup:

ついでに

Chrome 拡張機能のインストール

公式でオススメされている拡張機能 Vue Devtools を追加します。
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd

さいごに

インストールだけで相当時間がかかってしまいました。難しいです。
プロジェクトの作成はまた別の記事に分けて記録していこうと思います。

助けてもらったサイト

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

Vue Composition API で使えるリアクティブ関連のAPI一覧

Vue Composition APIのリファレンスを見たらリアクティブ周りのAPIが大量に追加されていたのでまとめてみました。

この記事は以下バージョン時点の情報です。

Vue Composition APIのAPI Reference に掲載されているいくつかのAPIは、まだVue2系で使えるComposition APIのプラグイン(vuejs/composition-api)でサポートされていません。
そのAPIについては :warning: マークを末尾につけています。サポート対応状況についてはこちらの記載をもとにしています。

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:warning:

オブジェクト(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 :warning:

依存関係の追跡と更新のトリガーを明示的に制御するカスタマイズされた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): T

shallowReactive

浅いリアクティブなオブジェクトを返します。ネストされたオブジェクトはリアクティブ化されません。

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 :warning:

浅い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 :warning:

オブジェクトが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 :warning:

オブジェクトが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くらいしか使ってなかったです。
今後はどんどん活用していきたいです。

参考

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

Vue Composition API リアクティブ周りの関数一覧

Vue Composition APIのリファレンスを見たらリアクティブ周りのAPIが大量に追加されていたのでまとめてみました。

この記事は以下バージョン時点の情報です。

Vue Composition APIのAPI Reference に掲載されているいくつかのAPIは、まだVue2系で使えるComposition APIのプラグイン(vuejs/composition-api)でサポートされていません。
そのAPIについては :warning: マークを末尾につけています。サポート対応状況についてはこちらの記載をもとにしています。

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:warning:

オブジェクト(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 :warning:

依存関係の追跡と更新のトリガーを明示的に制御するカスタマイズされた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): T

shallowReactive

浅いリアクティブなオブジェクトを返します。ネストされたオブジェクトはリアクティブ化されません。

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 :warning:

浅い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 :warning:

オブジェクトが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 :warning:

オブジェクトが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くらいしか使ってなかったです。
今後はどんどん活用していきたいです。

参考

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

Vue Composition API v1-beta で使えるリアクティブ関連のAPI一覧

Vue Composition APIのリファレンスを見たらリアクティブ周りのAPIが大量に追加されていたのでまとめてみました。

この記事は以下バージョン時点の情報です。

Vue Composition APIのAPI Reference に掲載されているいくつかのAPIは、まだVue2系で使えるComposition APIのプラグイン(vuejs/composition-api)でサポートされていません。
そのAPIについては :warning: マークを末尾につけています。サポート対応状況についてはこちらの記載をもとにしています。

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:warning:

オブジェクト(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 :warning:

依存関係の追跡と更新のトリガーを明示的に制御するカスタマイズされた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): T

shallowReactive

浅いリアクティブなオブジェクトを返します。ネストされたオブジェクトはリアクティブ化されません。

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 :warning:

浅い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 :warning:

オブジェクトが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 :warning:

オブジェクトが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くらいしか使ってなかったです。
今後はどんどん活用していきたいです。

参考

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

条件付きレンダリングって?

条件付きレンダリングてなだろう?

プログラミングにおいて複数の反応を用意して、一定の入力によって様々な出力へと変化するいわば制御構文というものがあります。これは一般的にはif文を用いることで可能になるんですが私が今勉強しているVue.jsで条件付きレンダリングにおいてはv-ifv-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で開くとこんな感じです...
スクリーンショット 2020-07-03 6.23.19.png
サーセン!背景のこと全く考えてなくて画像の境界線が分からなくなってるけど気にしないでくれると助かります
スクリーンショット 2020-07-03 6.23.35.png
プログラム自体は至って簡単なものです!
flagに格納されている値がtrueであればv-ifの方の表示がされます。ボタンを押すとdoActionのメソッドが動き、!data.flagによりdataのなかのflagの値の否定、つまりfalseへ変化しdata.flagへ格納します。したがって今度は値がfalseのためv-elseの方の表示がされます。
書いて見ればなんてことないただのif文でしたな〜

言葉に騙されないで!

参考書とかで勉強してると時々「は⁉︎何これ!初めて聞いたはこの機能!」ということが多々あると思います。でも実際に書いて実行してみると意外と今回のようになんてことない初歩中の初歩だったりする。なんか難しい言葉が出てきたな〜とか思ったらとにかくプログラムを実際に書いてみる!これに限ることを学んだ今日この頃...
この記事ほとんど書く必要なかった〜〜〜

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

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

原因は正直よくわかってない。

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