- 投稿日:2020-08-07T23:50:59+09:00
Nuxt.js × microCMS × Netlify でJamstackな構成のブログを作ってみた。
はじめに
ヘッドレスCMSを用いたブログサイトをJamstackな構成で作ってみたので、
それについてまとめておこうと思います。作ったサイトはこちらになります。
https://www.d-suke-notebook-blog.com/実行環境
macOS 10.15.5
node.js 12.18.2
Yarn 1.22.4
Nuxt.js 2.14.0Jamstackとは
実際にブログ作成の過程をまとめる前にJamStackについて簡単に触れておこうと思います。
JamStackは一言で言うとwebサーバーに依存しない構成のことですね。具体的には、事前にレンダリングしたコンテンツを配信して、動的な処理を行いたい場合はAPIで行います。
詳しい説明はこちらがおすすめです。
https://qiita.com/ozaki25/items/4075d03278d1fb51cc37これによって、ユーザーがアクセスした時にはただのHTMLでしかないので、
従来のwebアプリや、wordpressのようなCMSよりも応答速度が速くなります。
また、セキュリティの面においても攻撃対象がほぼないため、自然と高くなります。雛形の作成
まずはプロジェクトの雛形を作るため、create-nuxt-appを使います。
次の手順でインストール、使用します。$ npm i -g create-nuxt-app $ create-nuxt-app プロジェクト名いろいろ聞かれるので状況に合わせて必要なものを答えていきます。
ここではmodulesにaxiosとdotenvを指定します。これでプロジェクトの雛形テンプレートができました。
コンテンツの取得、表示
microCMSからデータを取得する処理について書いていきます。
データの保存場所
今回はvuexにデータを保存します。
その際、nuxtServerInit() で最初のサーバーとの通信の際に一回だけ取得し、
stateに保存します。
これは、ビルド時には一回取得すればいいため最初のアクセスだけ呼び出される
nuxtServerInit を使います。microCMSのブログの方ではasyncDataを使っていますが
その場合は取得済みのトークンなどを設定して過剰に通信しないようにすると
ビルド時間も短縮できると思います(無料枠には時間の制限があるため)。vuexの記述
storeディレクトリのindex.jsにモジュールモードで記述していきます。
export const actions = { async nuxtServerInit({ commit }, { $config }) { const resArticles = await this.$axios.$get(`${$config.apiUrl}blog`, { headers: { "X-API-KEY": $config.apiKey } }); const resTags = await this.$axios.$get(`${$config.apiUrl}tag`, { headers: { "X-API-KEY": $config.apiKey } }); commit("setArticles", resArticles.contents); commit("setTags", resTags.contents); } };非同期での通信になるのでactionsで行います。
データの流れとしてはactionsからmutationsを通してstateに保存、となっています。ここで注意する点として、API-keyの隠し方です。
こちらにあるようにhttps://microcms.io/blog/nuxt-secure-api-key
単純にprocess.env.API_KEY で取得すると、
ビルドされたファイルに定数に変換され残ってしまいます。なので、JamStack構成にする際の隠し方としては、nuxtのv2.13から追加された
privateRuntimeConfigを使います。nuxt.config.jsに
require("dotenv").config(); const { API_KEY } = process.env; const { API_URL } = process.env; export default { ~~~ privateRuntimeConfig: { apiKey: API_KEY, apiUrl: API_URL } ~~~ }と記述し、.envファイルには
API_KEY = API-keyを入力 API_URL = APIを入力とすることで$configから取得できます。
ページコンポーネントでデータを取得
vuexに保存したデータを実際にコンポーネントで使っていきます。
その際はgettersを使ってstateの値を取得するのが好ましいです。↓参考
https://uncle-javascript.com/vuex-gettersimport { mapGetters } from "vuex"; export default { ~~~ computed: { ...mapGetters(["articles"]) }, ~~~ }これで記事の配列が取得できるので一覧表示に使えます。
詳細ページはcomputed: { ...mapGetters(["articles"]), article() { const currentId = this.$route.params.id; return this.articles.find(article => article.url === currentId); } },こんな感じで該当の記事情報を取得すればいいと思います。
microCMSに、コンテンツのフィルター機能もあるので(fieldsパラメーター)
そちらを使うのもありだと思います。
今回はgettersでいろいろなパターンのフィルターをしています。これで実際に投稿した記事のデータを取得できるようになったので、あとは表示させるだけです。
公開する
構築が終わったら、公開作業をしていきます。
今回はnetlifyにアップしてgithubと連携することで自動でCI機能が使えるので
特にデプロイのためにする作業はありません。
(便利すぎますね、、、)netlifyを使わずに普通のレンタルサーバーにftp接続して自動アップロードさせる仕組みを
github actionで作ったこともあるので、それについてもいずれまとめるかもしれません(希望)。おわりに
個人的にJamstackな構成はとても理にかなっていて気に入っています。
特にユーザーごとに出し分ける必要がないコンテンツは事前にビルドしておく、
というのが必要最小限にまとまっていて好きですね。CMS以外にもECもヘッドレスの波が来ているので、これからのJamstackの広がりに期待です。
また、プレビュー画面の作り方についてはネットにあまり記事がないように思うので、
そちらは次あたりでまとめようと思います。今回作ったブログのコードはgithubに公開しています。
https://github.com/dc7290/d-suke-notebook-blog
- 投稿日:2020-08-07T20:22:18+09:00
【DRF + Vue.js 】API取得時のエラー(凡ミス)
はじめに
Vueのコンポーネント内から、DRFで作成したAPIを以下のようにaxiosなどで取得する際にエラーが発生した。
.../source/views/Mypage.vueexport default { ... ... mounted() { this.axios.get("/users/" + this.user_id).then(response => { this.Person = response.data }) } };発生したエラー
Access to XMLHttpRequest at 'http://127.0.0.1:8000/XXX/XXX/XXX' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.VueからのXMLHttpRequestのアクセスがDRF側でブロックされているという感じの内容である。
CORSの設定を確認すると、以下の様にしっかり設定できている。.../settings.pyCORS_ORIGIN_ALLOW_ALL = False CORS_ORIGIN_WHITELIST = ( 'http://localhost:8080', 'http://127.0.0.1:8080', )解決
リクエスト先のURLで最後に '/' をつけるのを忘れていた。
したがって、Vueのコンポーネントファイルを以下の様に修正すればよい。.../source/views/Mypage.vueexport default { ... ... mounted() { // 最後に'/'を追加する api.get("/users/" + this.user_id + '/').then(response => { this.Person = response.data }) } };
- 投稿日:2020-08-07T20:21:07+09:00
Vuetifyのbreakpointのnameはstringだけじゃない
結論
export type BreakpointName = number | 'xs' | 'sm' | 'md' | 'lg' | 'xl' export interface Breakpoint { name: BreakpointName // 省略 }問題提起
vuetify.breakpoint.name
にstring
を指定するとこんなエラーが出ていたThe types of 'name' are incompatible between these types. Type 'BreakpointName' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.stringだけじゃないのか??
Vuetifyの公式ドキュメントを読むとstringが帰ってくるのかと思った。
https://vuetifyjs.com/ja/customization/breakpoints/解説
Vuetifyのソースコードを読んでみよう
VueitfyのBreakpointの型
今回はブレークポイントのTypesだけ見れればいいので下記のリンクのファイルを確認
- どうやらBreakpoint.nameがBreakpointNameらしい
- BreakpointNameは
number | 'xs' | 'sm' | 'md' | 'lg' | 'xl'
が入る事がわかるhttps://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/types/services/breakpoint.d.ts
packages/vuetify/types/services/breakpoint.d.ts// Types export type BreakpointName = number | 'xs' | 'sm' | 'md' | 'lg' | 'xl' // Interfaces export interface Breakpoint { height: number lg: boolean lgAndDown: boolean lgAndUp: boolean lgOnly: boolean md: boolean mdAndDown: boolean mdAndUp: boolean mdOnly: boolean name: BreakpointName sm: boolean smAndDown: boolean smAndUp: boolean smOnly: boolean width: number xl: boolean xlOnly: boolean xs: boolean xsOnly: boolean mobile: boolean mobileBreakpoint: BreakpointName thresholds: BreakpointThresholds scrollBarWidth: number } export interface BreakpointOptions { mobileBreakpoint?: BreakpointName scrollBarWidth?: number thresholds?: Partial<BreakpointThresholds> } export interface BreakpointThresholds { xs: number sm: number md: number lg: number }なぜBreakpointNameにNumberが入るの?
先程のコードの抜粋を見るとnameとmobileBreakpointにBreakpointNameが使われているらしい。
packages/vuetify/types/services/breakpoint.d.tsexport type BreakpointName = number | 'xs' | 'sm' | 'md' | 'lg' | 'xl' export interface Breakpoint { name: BreakpointName mobileBreakpoint: BreakpointName }datatableのpropsを例に説明
どうやら下記の様に利用されるらしい。
defaultでは600が入っているが自由に変更できるとのこと。https://vuetifyjs.com/ja/components/data-tables/
<v-data-table :headers="headers" :items="desserts" :items-per-page="5" class="elevation-1" mobile-breakpoint="500" />VuetifyのmobileBreakpointが利用されているソース
また、mobileBreakpointがnumberだけならBreakpointNameで統一せずに分けろよな!!
と思ったがmobileBreakpointmにlgのようstringを使うコードも一応存在するみたい。breakpoint.nameにnumber入れてるコードあるかな?
ということでVuetifyのbreakpointのテストコードを拝見
https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/services/breakpoint/__tests__/breakpoint.spec.tsnumber入れてるテスト無いじゃん!!
- 投稿日:2020-08-07T19:25:02+09:00
【Vue.js】Vue CLIで作成したページのtitleとdescriptionを変更する方法
【Vue.js】Vue CLIで作成したページのtitleとdescriptionを変更する方法
Vue CLIで作成したページはデフォルト状態だと、titleはプロジェクト名で全ページ共通。descriptionは存在しない状態になっている。
routerで各ページのtitleとdescriptionを設定できるようにする。
・参考にしたページ
- https://www.sky-limit-future.com/entry/vue_title_desc_tag
- https://router.vuejs.org/ja/guide/essentials/navigation.html
- https://router.vuejs.org/ja/guide/advanced/meta.html目次
- 作業手順
- ルートにmetaフィールドを追加
- 共通コンポーネントapp.vueにtitleとdescriptionをセットするメソッドを追加
- 共通コンポーネントapp.vueにmountedオプションをセット
- 共通コンポーネントapp.vueにwatchオプションをセット
- 共通テンプレート(pubulic>index.html)にdescription属性を追加
- 確認用ソースコード
(参考)Vue CLIによるWEBページの作成方法
作業手順
- ルートにmetaフィールドを追加
- 共通コンポーネントapp.vueにtitleとdescriptionをセットするメソッドを追加
- 共通コンポーネントapp.vueにmountedオプションをセット
- 共通コンポーネントapp.vueにwatchオプションをセット
- 共通テンプレート(pubulic>index.html)にdescription属性を追加
1. ルートにmetaフィールドを追加
各ルートにmetaフィールドを追加して任意のプロパティをセットすることができる。
これらの情報はrouteインスタンスに格納される。
プロパティ名はわかりやすいように、title->title, description->descとする(任意)。
ファイル
下記のmeta部分を記述する。
scriptconst routes = [ { path: '/', name: 'Home', component: Home, meta: { title: 'ページタイトル', desc: 'ディスクリプションを記述' } } ]
2. 共通コンポーネントapp.vueにtitleとdescriptionをセットするメソッドを追加
methodsオプションに下記を記述。
プロパティへのアクセス方法はルートインスタンス.meta.設定した変数名
titleをセットするメソッド
- メソッド名: createTitleDesc
- 引数でrouteインスタンスを渡す
- 変数routeInstanceに代入している(変数名は任意)
- 後述のmountedオプションで代入している
- route.meta.titleで設定されているtitleを取得
- if分岐でtitleが設定されていれば、後ろにサイト名をつけた文字列をsetTitlに格納。
- titleタグ(document.title)にsetTitleを代入する。
App.vueexport default { methods : { createTitleDesc : function(routeInstance){ // タイトルを設定 if(routeInstance.meta.title){ var setTitle = routeInstance.meta.title + ' | サイト名など(任意)'; document.title = setTitle; } else { document.title = 'ルートでtitleがセットされていない場合に表示するテキスト' } }
descriptionをセットするメソッド
基本的にはtitleのメソッドと同じ。
- descriptionはmetaタグのname属性なので、
querySelecter
で要素を指定。- descriptionの値はcontent属性に記述するため
setAttribution
で設定する。App.vueexport default { methods : { createTitleDesc : function(routeInstance) // メタタグdescription設定 if(routeInstance.meta.desc){ var setDesc = routeInstance.meta.desc + ' | MIYACHIN VUE'; document.querySelector("meta[name='description']").setAttribute('content', setDesc) } else { document.querySelector("meta[name='description']").setAttribute('content', 'description is not set') } } } }3. 共通コンポーネントapp.vueにmountedオプションをセット
mountedオプションは早い段階で実行(コンポーネントやDOMを読み込むよりも早い)される。
- routeインスタンス($rounte)を変数(routeInstance)に代入する。
- 上記で設定したtitleとdescriptionをセットするメソッド(createTitleDesc)を実行する。
App.vueexport default { mounted : function(){ var routeInstance = this.$route; this.createTitleDesc(routeInstance); } }4. 共通コンポーネントapp.vueにwatchオプションをセット
titleやdescriptionに変更があった場合に、変更内容を自動反映するようwatchオプションを追加する。
App.vueexport default { watch: { '$route' (routeInstance, from) { this.createTitleDesc(routeInstance); } } }App.vueのJSソースコード
上記1〜4を合わせたコードは下記になる。
App.vue<script> //routerで設定したタイトルとめたタグを反映する export default { methods : { createTitleDesc : function(routeInstance){ // タイトルを設定 if(routeInstance.meta.title){ var setTitle = routeInstance.meta.title + ' | MIYACHIN VUE'; document.title = setTitle; } else { document.title = 'title is not set' } // メタタグdescription設定 if(routeInstance.meta.desc){ var setDesc = routeInstance.meta.desc + ' | MIYACHIN VUE'; document.querySelector("meta[name='description']").setAttribute('content', setDesc) } else { document.querySelector("meta[name='description']").setAttribute('content', 'description is not set') } } }, mounted : function(){ var routeInstance = this.$route; this.createTitleDesc(routeInstance); }, watch: { '$route' (routeInstance, from) { this.createTitleDesc(routeInstance); } } } </script>5. 共通テンプレート(pubulic>index.html)にdescription属性を追加
App.vueファイルでdescriptionを設定するためのmetaタグにアクセスする記述をしたが、デフォルトのhtmlテンプレートにはタグが存在しないため追記する。
publicフォルダのindex.htmlのheadタグ内に記載する。
.html<meta name="description">以下の記述で上記タグを参照することができる。
document.querySelector("meta[name='description']")
確認用ソースコード
(1)router > index.js
index.jsVue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home, meta: { title: 'ページタイトル', desc: 'ディスクリプションを記述' } } ] })
(2)ルートディレクトリ > App.vue
App.vue<script> //routerで設定したタイトルとめたタグを反映する export default { methods : { createTitleDesc : function(routeInstance){ // タイトルを設定 if(routeInstance.meta.title){ var setTitle = routeInstance.meta.title + ' | MIYACHIN VUE'; document.title = setTitle; } else { document.title = 'title is not set' } // メタタグdescription設定 if(routeInstance.meta.desc){ var setDesc = routeInstance.meta.desc + ' | MIYACHIN VUE'; document.querySelector("meta[name='description']").setAttribute('content', setDesc) } else { document.querySelector("meta[name='description']").setAttribute('content', 'description is not set') } } }, mounted : function(){ var routeInstance = this.$route; this.createTitleDesc(routeInstance); }, watch: { '$route' (routeInstance, from) { this.createTitleDesc(routeInstance); } } } </script>
(3) public > index.html
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <!-- 以下を追加 --> <meta name="description"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
以上。
- 投稿日:2020-08-07T18:54:29+09:00
【Nuxt.js】Nuxt実践編:あると便利なコピーボタンの作り方
? この記事はWP専用です
https://wp.me/pc9NHC-xv前置き
ここ最近は文法編に力を入れていたので
久しぶりの実践編です✨input内のテキストを
コピーするボタンを作ってみましょう♪
イベントアプリなどでページを作成し、
自動生成されたurlをコピーしたい時なんかに使えます?urlの自動生成まではやりません。
あくまでinputとコピーボタンのみ作成します??
スタイリングも省いてます、そこはお好みで♪まずはinputを作成
コピーボタンの作り方は
3通り用意していますが、
input部分はどれも共通です?divではダメなのか
今回はコピーボタンがあるので
値が見えなくても@clickでコピーさえできればOK⭕️
そのためinputでやっていますが、
urlを直接選択するような場合は
スクロールの効くdivで作成すると良いと思います?divで作る場合も後述しておきます✍️
解説
readonly属性
urlを間違って編集したりしないようにします⚠️:value
今回はユーザーが入力する必要がないので
@inputが不要、双方向バインディングが不要です?
値だけバインドできればOK⭕️class="text"
input要素ではありますが、
入力不要でテキスト表示の役割のため。
コンポーネントにする場合も
InputText.vueなどの命名にし、
$attrsを使うのが良いと思います?⬇️双方向バインディング、
v-modelについてはこちら
https://wp.me/pc9NHC-kIコード
index.vue<template> <div class="page"> <input id="input" :value="url" type="text" readonly class="text" > </div> </template> <script> export default { data () { return { url: 'https://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb', } }, } </script>コピーボタンを作る
コピーと言えばdocument.execCommand()ですが廃止!
なのでClipboard APIと
nuxt-clipboard2を使ってみましょう?document.execCommand()も
一応…最後に載せています?方法1: Clipboard API
? 続きはWPでご覧ください?
https://wp.me/pc9NHC-xv
- 投稿日:2020-08-07T17:35:22+09:00
Could not find a declaration file for module 'vee-validate/dist/locale/ja.json'. で怒られる。
結論
types/vee-validate.d.tsdeclare module "vee-validate/dist/locale/*";問題提起
VeeValidateの日本語設定を行っているファイルでこんなエラーが出ました。
EnglishCould not find a declaration file for module 'vee-validate/dist/locale/ja.json'.Japaneseモジュール 'vee-validate/dist/locale/ja.json' が見つかりません。VeeValidateはTypeScriptで書かれているハズ…
最初はTS非対応のプラグインでよくある
@types/{pluginName}
を探しました。
しかし、有りません。それもそのハズ、Veevalidateは既にTypeScriptで書かれています。
https://github.com/logaretm/vee-validateまた、エラーが表示されるのはlocal言語指定しているjsonファイルの呼び出し部分のみです。
nodo_modules以下を見てみる
$ cat node_modules/vee-validate/dist/locale/ja.json結果はGitHubのこのページと同様のコードが表示されました。
https://github.com/logaretm/vee-validate/blob/master/locale/ja.json解決
"vee-validate/dist/locale/*"
のパスの型をグローバルに宣言してあげます。types/vee-validate.d.tsdeclare module "vee-validate/dist/locale/*";おわりに
正直なぜ宣言ファイルを見つけられなかったのかも、
Pathに対するグルーバルな型指定でエラー消えるのかも理解できていません。もし理解された方や他のもっといい方法があるという方がいらっしゃいましたら
コメントにてご指摘頂けると幸いです。参考
GitHubの問題解決issue
https://github.com/logaretm/vee-validate/issues/1477Veevalidateの型宣言ファイル
https://github.com/logaretm/vee-validate/blob/master/src/types.ts
- 投稿日:2020-08-07T15:42:46+09:00
【Vue.js】Vue CLIとは?ゼロからPJを立ち上げ作成したページを表示する方法
【Vue.js】Vue CLIとは?ゼロからPJを立ち上げ作成したページを表示する方法
開発支援ツールであるVue CLIを使えるようにするための環境構築方法から実際にブラウザに表示するまでの流れ。
目次
- Vue CLIとは?
- Vue CLIでできること
- 開発に必要なもソフトウェア
- Node.jsのインストール
- Vue CLI v3のインストール
- 新規プロジェクトの作成
- PJのファイル・ディレクトリ構成
- serveコマンドの処理の流れ
- 単一ファイルコンポーネント
- 単一ファイルコンポーネントの作成と表示
Vue CLIとは?
CLIはCommand Line Interfaceの略。コマンドラインを使って開発を行うためのツール。
言語はVue.jsを使うため、Vue CLIと呼ぶ。Vue.js公式のツール。
Vue CLIでできること
Vue CLIを使うことで下記のようなことができる。
各機能を自分で作ることもできるが労力がかかる。よく使う機能を予め用意してくれているありがたいもの。
- プロジェクトのテンプレートの作成
- 複数のjsファイルを一つにまとめる
- .vueファイルを.jsに変換する
- トランスパイル
- JavaScriptの構文チェック
- テストツールの導入など
トランスパイルとは?
与えられたコードを別の言語に変換すること。プログラミングの翻訳。
JSでは、古い書き方にしか対応していないブラウザ用に、言語を変換することを指す。
<もう少し細かく>
JSは2015年に仕様が大きく変わった。その時に公表されたJSの仕様をES2015と呼ぶ。それ以前の使用がES系。一部のブラウザでは、このES2015以降の書き方で書かれたJSを読み込めないため変換を行う。これがトランスパイルの主な目的。
トランスパイルの変換ソフト(トランスパイラ)にはBabelを使うのが一般的。
(私感)ES5からES2015になったのが2015年でもう5年以上経過しているので、いい加減気にしなくてもいいのではと思う。
開発に必要なもソフトウェア
- Node.js v8.9以上
- Vue CLI 3系のNPMパッケージ
- テキストエディタ
Node.jsのインストール
Node.jsとパッケージ管理ソフトのNPMをインストールする。
NPMはNode.jsのソフトの中に入っている。Node.jsとは?
サーバーサイドのJavaScript。サーバー操作をJavaScript言語を用いてできるようにしたのがNode.js。
JSはブラウザ画面上での処理を記述するというのが基本的な使い方。これをサーバーサイドの言語であるPHP、Ruby、Pythonなどの仲間入りさせたのが、Node.js。
NPMとは?
Node Package Managerの略。Nodeで使える便利なパッケージの管理ツール。
パッケージのインストールやバージョンアップなどができる。
主なサーバーサイド言語(&OS)とパッケージ管理ツール一覧
言語 パッケージ管理ソフト Node.js npm Ruby gem Python pip Linux yum PHP composer Mac homebrew Windows Chocolatey/Windows Package Manager
Node.jsのインストール実操作(macの場合)
#インストール済みか確認(バージョンが表示されればインストール済み) $ node -v #npmがインストール済みか確認 $ npm -v #homebrewがインストール済みか確認 $ brew -v #homebrewのインストール(5分ぐらい) $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" ==> The Xcode Command Line Tools will be installed. Press RETURN to continue or any other key to abort // Enterキーをクリックし、PCのパスワードを入力 ==> Next steps: - Run `brew help` to get started //完了 #nodebrewのインストール(10秒ぐらい) $ brew install nodebrew #nodebrewのバージョン確認 $ nodebrew -v #nodebrewでインストールできるnode.jsのバージョン確認 $ nodebrew ls-remote #node.js用にディレクトリを作成 $ mkdir -p ~/.nodebrew/src #node.js公式サイトで確認したLTSバージョンをインストール $ nodebrew install-binary v12.18.3 #nodebrewのバージョンを確認 $ nodebrew ls v12.18.3 current: none //現在の有効になっているバージョンがない状態 #node.jsの使用するバージョンを指定 $ nodebrew use v12.18.3 use v12.18.3 //設定完了 #node.jsのパスを通す(bashを利用) echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile #パスを通した設定を反映 $ source ~/.bash_profile #node.jsのバージョンを確認 $ node -v v12.18.3 //指定したバージョンが表示されれば完了homebrewとは?
Mac OSのパッケージ管理ソフト。
・homebrew公式サイト
https://brew.sh/index_ja.htmlnodeberwとは?
node.jsのバージョン管理ソフト。
homebrewでもバージョン管理できるが、古いバージョンをインストールし直した時にパスを通しなおすなど作業が必要。
nodebrewでバージョンを変更すれば、面倒な設定を合わせて実行してくれる。
Node.jsのLTSとは?
LTSとはLong Term Supportの略。
ソフトには最新版と、長期サポート対象が存在する。基本的にインストールはLTSバージョンにする。node.jsのLTSは公式ページで確認できる。
https://nodejs.org/ja/
Vue CLI v3のインストール
今回は3系を使うためバージョンを指定してインストール。
(2020年8月時点の最新版はv4.5.1)#Vue CLIがインストールされているか確認 $ vue --version #Vue CLIをインストール(約30秒) npm install -g @vue/cli@3.9.3 #Vue CLIがインストールされているか確認 $ vue --versionnpmのインストールコマンドの内容
npm install -g @vue/cli@3.9.3
┗ オプション -g:グローバル(どこからでも使える)にする。
┗ @vue/cli: 最新版をインストール
┗ @vue/cli@バージョン: バージョンを指定してインストール・Vue CLIのバージョンリリース状況
https://github.com/vuejs/vue-cli/releasesVue CLI最新版のインストール方法
v3 -> v4へのアップグレードを含む
#最新版のインストール $ npm install -g @vue/cli@next #既存プロジェクトをアップデート $ vue upgrade --next
新規プロジェクトの作成
Vue CLIでプロジェクトを作成
#(必要に応じて)フォルダの作成 $ mkdir ディレクトリパス #PJフォルダを作成するディレクトリに移動 $ cd ディレクトリパス #Vue CLIでPJを作成 vue create PJ名vue createを実行すると、現在いるディレクトリにPJフォルダが作成される。
Vue CLI対話モード
vue createでPJを作成すると、Vue CLIの対話モードに移る。
(1)確認用のシンプルな設計とするため、Manually Select featuresを選択する。
(2)PJに必要なパッケージを選択する。
以下3つのパッケージにチェックを入れる。(spaceキーで選択切り替え)
- Babel:トランスパイラ
- Router:ルーティングのためのVueルーター
- Vuex:バージョン管理ソフト
チェックしてないソフトも、必要になったタイミングで別途インストール可能。
(3)historyモードを使用する(YES)
(4)ソフトの設定ファイルを個別に管理する(In dedicated config files)
(5)現在の設定をプリセットとして保存しない(No)
(6)インストール完了まで待機(約20秒)
(7)PJディレクトリに移動し、npmサーバーを起動する
指示されたコマンドを実行
$ cd PJ名 $ npm run serve(7)ページを開く
Localで与えられたURLを開く。
http://localhost:8080/
ウェルカムページが表示されれば完了
PJのファイル・ディレクトリ構成
node_modulesフォルダ
インストールしたライブラリが保存されている。
publicフォルダ
WEB上に公開されるフォルダ。ドキュメントルートとなる。
srcフォルダ
開発者用のリソースを保存するフォルダ。
各種ファイル
ファイル名 概要 .browserslistrc babelのサポートするブラウザのバージョンが記述されている。(読み方: .browsers list rc) .gitignore gitの無視リスト babel.config.js babelの設定ファイル package-lock.json npmの依存パッケージの設定 postcss.config.js postcssの設定ファイル package-lock.jsonファイル
npmのコマンドの対応表や、使用するパッケージ情報が記載されている。
▼コマンド
例えば、npm run serve
でnpmを起動したが、実際は、
npm run vue-cli-service serve
が実行されている。
・serve:開発用の環境用のコマンド
・build:本番公開用のコマンド▼パッケージバージョン情報
・dependencies: 使用するnpmパッケージ
・devDependencies: 開発モードでのみ使用するnpmパッケージ
serveコマンドの処理の流れ
npm run serve
で実行される処理の流れは、(1)
vue-cli-service serve
コマンドを実行
"serve": "vue-cli-service serve",(2)publicフォルダ内をドキュメントルートとして公開
(3)htmlファイルのテンプレートに内容が表示される。
.html<div id="app"></div>(4) srcフォルダ内のmain.jsが反映される
.jsnew Vue({ router, store, render: h => h(App) }).$mount('#app')(5) renderオプションでappコンポーネントを表示する
.jsrender: h => h(App)(6)appコンポーネントはsrcのapp.vueに記述されている。
app.vueにはHTML, css, jsが記述されており、その内容をテンプレートに表示している。
単一ファイルコンポーネント
単一ファイルコンポーネントとは?
拡張子が「.vue」のファイルを単一ファイルコンポーネントと呼ぶ。vue.jsのコンポーネントを個別のファイルに切り出したもの。
html, css, jsを記述することが可能。
単一ファイルコンポーネントのコード例
.vue<template> <div id="app"> <!-- htmlタグを記述 --> </div> </template> <style> /*cssを記述*/ </style> <script> //jsを記述 </script>単一ファイルコンポーネントのビルド
ビルドとは?
ソースコードのバグ検証後に実行可能なファイルに変換すること。
ブラウザはvueファイルを読み込めないため、ブラウザに表示するにはビルドが必要になる。
Vue CLIにおけるビルドの方法
(1)Webpack と (2)Vue-Loaderを使う
webpackとは?
自分で作成したスクリプトとライブラリとして読み込んでいるスクリプトを合体させるソフトウェア。Vue-Loaderとは?
vueコンポーネントをjsファイルに変換する。
Webpackのローダー。
単一ファイルコンポーネントの作成と表示
単一ファイルコンポーネントの作成
こんにちは! と表示するファイルを作成する。
(1)srcフォルダのcomponentsフォルダの中に、konnichiwa.vueファイルを作成
(2)templateを作成する。
- HMTL:templateタグで囲む
- style: scoped属性をつけると現在のテンプレートにのみ適用となる.vue<template> <div> <h1>こんにちは!</h1> 新たにテンプレートを作成し読み込み成功 <hr> </div> </template> <script> </script> <style scoped> </style>作成した単一ファイルコンポーネントの表示
Home.vue内で作成したファイルの公開を行う場合。
(1)srcフォルダ > Viewsフォルダ > Home.vueファイルを開く
(2)vueファイルをインポートする
import コンポーネント名 from '@/components/ファイル名'
Home.vueimport Konnichiwa from '@/components/Konnichiwa.vue'
(3) componentsオプションに設定したコンポーネント名を追記Home.vueexport default { name: 'Home', components: { HelloWorld, Konnichiwa }
(4) templateタグ内にコンポーネント名を追記
<コンポーネント名/>
Home.vue<template> <div class="home"> <Konnichiwa/> <img alt="Vue logo" src="../assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> </div> </template>
(5)サーバーを起動
npm run serve
▼読み込みに成功
http://localhost:8080/
(5)サーバーの終了
ctrl + c
以上。
- 投稿日:2020-08-07T14:18:57+09:00
VueとFirebaseでWebアプリ開発でハマったこと②: javascriptやnpmなどのエラー
前回『VueとFirebaseでWebアプリ開発でハマったこと①: Strings must use singlequote, Extra semicolon』の続き。
概要
firestoreからデータを受け取ってVueで表示し、Vueでデータを送信してfirestoreに保存する一連の流れをInputしたいと思い、『firestore, vue.jsでリアルタイム同期のチャットを実装してみる [チュートリアル形式]』という記事を参考に作業していたところ次の3つのエラーにハマったのでどう解決したのかまとめる。
エラー1: Uncaught TypeError: Cannot read property 'install' of undefined
上記は、
$ yarn serve
の結果は正常だけどもアクセス後にエラーになるケースでした。
このエラーだけでは状況がわからないので、エラー文3行目に対応箇所であったfirebase.js
が表示されているためこのリンクをクリックするとConsole上にそのソースコードとエラーの箇所まで飛んでくれました。原因としては、VueがFirebaseの機能を扱う初期化で問題が起きていることが分かります。
ググったところvuefireのissue『App startup failure with VueFire plugin #286』を見つけ、次のように対応するとエラーが解消できました。- import VueFire from 'vuefire' - Vue.use(VueFire) + import { firestorePlugin } from 'vuefire' + Vue.use(firestorePlugin)エラー2: [Vue warn]: Error in beforeCreate hook: "Error: Vuetify is not properly initialized, see https://vuetifyjs.com/ja/getting-started/quick-start/#bootstrapping-the-vuetify-object"
正確には他にも複数エラーが出てますが、エラーの解決は基本的に最初のエラーから対応するべきなのでこのエラーに集中しました。
原因は、参考にした記事のプラグインのインストールにあたるところで、ただプラグインを入れるだけ解説されてますが、結局はこれだけでは不十分でちゃんと
vuefire
のインストール対応が必要でした。エラー文にも出ている通り、公式の
vuefire
のクイックスタートにアクセスして「Nuxtのインストール」「Webpackのインストール」の対応が必須でした。エラー3: These dependencies were not found
$ yarn serve ... ERROR Failed to compile with 3 errors 12:48:50 These dependencies were not found: * firebase/app in ./src/plugins/firebase.js * firebase/firestore in ./src/plugins/firebase.js * vuefire in ./src/plugins/firebase.js To install them, you can run: npm install --save firebase/app firebase/firestore vuefire色々触っているとたまにこのエラーが出てたんですが、これは単に
./node_modules
を削除して再度$ npm install
すると直りました。
- 投稿日:2020-08-07T14:18:57+09:00
VueとFirebaseでWebアプリ開発でハマったこと②:
前回『VueとFirebaseでWebアプリ開発でハマったこと①: Strings must use singlequote, Extra semicolon』の続き。
概要
firestoreからデータを受け取ってVueで表示し、Vueでデータを送信してfirestoreに保存する一連の流れをInputしたいと思い、『firestore, vue.jsでリアルタイム同期のチャットを実装してみる [チュートリアル形式]』という記事を参考に作業していたところ次の3つのエラーにハマったのでどう解決したのかまとめる。
エラー1: Uncaught TypeError: Cannot read property 'install' of undefined
上記は、
$ yarn serve
の結果は正常だけどもアクセス後にエラーになるケースでした。
このエラーだけでは状況がわからないので、エラー文3行目に対応箇所であったfirebase.js
が表示されているためこのリンクをクリックするとConsole上にそのソースコードとエラーの箇所まで飛んでくれました。原因としては、VueがFirebaseの機能を扱う初期化で問題が起きていることが分かります。
ググったところvuefireのissue『App startup failure with VueFire plugin #286』を見つけ、次のように対応するとエラーが解消できました。- import VueFire from 'vuefire' - Vue.use(VueFire) + import { firestorePlugin } from 'vuefire' + Vue.use(firestorePlugin)エラー2: [Vue warn]: Error in beforeCreate hook: "Error: Vuetify is not properly initialized, see https://vuetifyjs.com/ja/getting-started/quick-start/#bootstrapping-the-vuetify-object"
正確には他にも複数エラーが出てますが、エラーの解決は基本的に最初のエラーから対応するべきなのでこのエラーに集中しました。
原因は、参考にした記事のプラグインのインストールにあたるところで、ただプラグインを入れるだけ解説されてますが、結局はこれだけでは不十分でちゃんと
vuefire
のインストール対応が必要でした。エラー文にも出ている通り、公式の
vuefire
のクイックスタートにアクセスして「Nuxtのインストール」「Webpackのインストール」の対応が必須でした。エラー3: These dependencies were not found
$ yarn serve ... ERROR Failed to compile with 3 errors 12:48:50 These dependencies were not found: * firebase/app in ./src/plugins/firebase.js * firebase/firestore in ./src/plugins/firebase.js * vuefire in ./src/plugins/firebase.js To install them, you can run: npm install --save firebase/app firebase/firestore vuefire色々触っているとたまにこのエラーが出てたんですが、これは単に
./node_modules
を削除して再度$ npm install
すると直りました。
- 投稿日:2020-08-07T14:11:43+09:00
Vue.jsでaxiosを使ってぐるなびAPIを取得する
環境
macOS Catalina 10.15.6
vue 2.6.11
vue/cli 4.4.6ぐるなびAPIを取得
こちらのページから新規アカウントを発行しアクセスキーを取得します。
https://api.gnavi.co.jp/api/登録したメールアドレスにアクセスキーが送られてくるのでそれを使います。
ぐるなび WEB SERVICEのAPIテストツールを使うとわかりやすいです。keyidに先ほど取得したアクセスキーを入力し、取得したいパラメータを[追加]を押して選びます。
URLの欄に入っているURLを実際のプログラムでも使います。
クエリを送信を押すと取得できる情報がjson形式で返ってきます。
axiosをインストール
$ npm install axios --save無事にインストールが完了するとpackege.jsonに"axios"が追加されているので任意のvueファイルでaxiosを読み込む
import "axios" const axios = require('axios').default; async mounted(input){ try{ const response = await axios.get(APIテストツールで生成したURL", { params: { freeword:”居酒屋” }}) this.info= res.data.rest; }catch(error){ alert(error.message) } }axios.get()の引数にAPIテストツールで生成したURL渡す。
URLの末尾にparams:{}を追記して取得したい情報を選択します。
今回はfreewordをキーとしています。
他にも色々種類があるので詳しく知りたい方はこちらをご覧ください。
https://api.gnavi.co.jp/api/manual/foreignrestsearch/今回はfreeword直接検索ワードを渡していますが、実際はv-modelでフォームから取得した値を渡してあげるのが一般的かと思います。
これで変数responseの中に取得した情報が入っているので好きな様に加工して表示する形になります。
- 投稿日:2020-08-07T13:39:25+09:00
VeeValidateを使ってVueアプリでバリデーションをする
VeeValidate
VeeValidateは以下の特徴を備えたVue.jsのバリデーションライブラリーです。
ドキュメントを読みつつ訳しながらまとめた内容です。https://logaretm.github.io/vee-validate/
- テンプレートベースのバリデーション
- 多彩なバリデーションルールがデフォルトで用意済み
- 一級のローカライゼーションサポート
- HTML5のinputフォームとカスタムコンポーネントのバリデーションが可能
- カスタムルールとエラーメッセージも作成可能
はじめよう
利用方法
まずVueのインスタンスに登録します。
import Vue from 'vue'; import VeeValidate from 'vee-validate'; Vue.use(VeeValidate);コンフィグ設定したいならこちらの方法。
import Vue from 'vue'; import VeeValidate from 'vee-validate'; const config = { // config that you want } Vue.use(VeeValidate, config);ベーシックな例
inputフォームに
v-validate
ディレクティブとname
属性を追加するだけでOKです。name
属性はエラーメッセージの生成に利用されます。
v-validate
ディレクティブにはルールを設定します。パイプ|
を間に挟むことで複数のルールが適用可能です。
以下は「必須、かつ、eメールの形式であること」の例です。<input v-validate="'required|email'" name="email" type="text">エラーメッセージを表示するには
errors.first
メソッドを利用することでその項目の最初のエラーが取得できます:<span>{{ errors.first('email') }}</span>Tips
v-validate
に定義された値はシングルクォートで囲われています。これはディレクティブに与えられた内容はJavaScriptとして評価されるためです。
シングルクォートで囲むことでString
型であることを明示的にする必要があります。
v-validate="required"
としてしまうとrequired
というおそらく存在しないであろう変数や関数を参照しようとしてしまいます。シンタックス
バリデーションルールは
String
型での定義や、プログラマブルにしたい時はObject
型での定義も可能です。// String const single = 'required' const multiple = 'required|numeric' // Object const single = { required: true } const multiple = { require: true, numeric: true }ルールのパラメーター
ルールの一部はパラメーターを持つことができます。パラメーターは複数の定義方法が可能です。
String
型ならカンマ区切りの値Object
型なら配列の値Object
型でより複雑なパラメーターならオブジェクト// String型のパラメーター const someRule = 'included:1,2,3,4'; // Array型のパラメーター const someRuleObj = { included: [1, 2, 3, 4] }; // Object型のパラメーター const someCplxObj = { email: { allow_utf8_local_part: true } };ルールの実例
上の内容を実例でみてみましょう。
- 必須のEメールの入力欄
<input v-validate="'required|email'" type="email" name="email"> <input v-validate="{ required: true, email: true }" type="email" name="email">
- 必須でないユーザー名の入力欄
<input v-validate="'alpha'" type="text" name="username"> <input v-validate="{ alpha: true }" type="text" name="username">
- 必須かつ最低6文字のパスワード入力欄
<input v-validate="'required|min:6'" type="password" name="password"> <input v-validate="{ required: true, min: 6 }" type="password" name="password">エラーの表示
エラーメッセージが生成されると、エラー表示簡易化のために用意された
ErrorBag
インスタンスに保存されます。デフォルトでは
ErrorBag
のインスタンスはerror
という名前で各コンポーネントのcomputed
プロパティにインジェクトされます。
error
という名前は競合を避けるためにコンフィグからカスタマイズ可能です。エラーメッセージを1つ表示する
一般的には1つのフォームに対して1つのエラーを表示することが多いと思います。これは
errors.first('fieldName')
メソッドでエラーを取得可能です。<input type="text" name="fieldName" v-validate="'required'"> <span>{{ errors.first('fieldName') }}</span>Tips
VeeValidateのデフォルトでは1フィールドに対して1エラーしか生成しません。これはバリデーションパイプラインの高速化が理由です。
複数のバリデーションエラーのメッセージを同時に生成したい場合はfastExit
オプションをコンフィグからカスタマイズしてください。
あるいは、特定のフォームだけに適用したい場合はcontinues
modifilerの適用も可能です。v-validate.continues="'required|numeric'"
エラーメッセージを複数表示する
1つのフォームに複数のエラーを表示したい場合は
errors.collect('fieldName')
メソッドが利用できます。
このメソッドは1つのフィールドに紐づくエラーメッセージを配列で返します。<input type="text" name="fieldName" v-validate.continues="'required|alpha|min:5'"> <ul> <li v-for="error in errors.collect('fieldName')">{{ error }}</li> </ul>全てのエラーメッセージを表示する
errors.all()
を使いフラットなエラーメッセージの配列を取得できます。<input type="text" name="first" v-validate.continues="'required|alpha|min:5'"> <input type="text" name="second" v-validate.continues="'required|alpha|min:5'"> <ul> <li v-for="error in errors.all()">{{ error }}</li> </ul>フィールド名ごとにグルーピングしたい場合は、
errors.collect()
を引数無しで利用します。
キーがフィールド名で値がエラーメッセージの配列のオブジェクトが取得できます。<input type="text" name="first" v-validate.continues="'required|alpha|min:5'"> <input type="text" name="second" v-validate.continues="'required|alpha|min:5'"> <ul> <li v-for="group in errors.collect()"> <ul> <li v-for="error in group">{{ error }}</li> </ul> </li> </ul>バリデーションルール
いっぱいあるよ。
https://baianat.github.io/vee-validate/guide/rules.html
カスタムコンポーネントのバリデーション
VeeValidateはHTML5のinput要素だけでなくカスタムコンポーネントのバリデーションも対応しています。
カスタムコンポーネントでの利用はいくつか注意が必要です。このトピックではその注意点について説明します。どう動くか
カスタムコンポーネントを入力欄として動作させるのに
v-model
を利用して値を扱うことは一般的です。
VeeValidateはこの値を$watch
APIを利用して監視していますが、$watch
には制限があります。
たとえば、v-for
のイテレーターの値はv-for
のループのコンテキストとしてしか存在していないため監視できません。VeeValidateは
value
のpropを監視するようフォールバックします。
もしコンポーネントがmodel
コンストラクタをカスタマイズしている場合でも、正しいプロパティを検出しそれを監視します。このデモはVeeValidateとVuetifyのカスタムコンポーネントを組み合わせた例です。
以上はほとんどのケースをカバーしますが、
v-model
を利用して値を取り回していない場合は対応できません。
そのようなケースではコンポーネントのコンストラクターオプションを利用することでより細かなバリデーションの挙動を設定することができます。コンポーネントのコンストラクターオプション
バリデーターのカスタムコンポーネントへの挙動をカスタマイズすることにより不要な属性の追加などを防ぐことができます。
これは$_veeValidate
フィールドをカスタムコンポーネントに定義することで実現します:export default { // ... data: () => ({ // コンポーネント内部のデータ innerValue: 'initial' }) $_veeValidate: { // バリデーションの対象とするデータ value () { return this.innerValue; } }, }
$_veeValidate
ではvalue
含めて以下が設定可能です。
プロパティ 種類 初期値 説明 name () =>
,string
undefined
エラーメッセージの生成に使われる名前 value () =>
,any
undefined
バリデーションの対象となる値 rejectFalse boolean
false
false
がrequired
に対してバリデーションエラーとなるかevents string
`input blur` validator string
new
ornull
コンポーネントがバリデータのインスタンスをどう得るか。 new
の場合は新しいバリデーターインスタンスを生成して利用する。
- 投稿日:2020-08-07T12:18:50+09:00
【Vue】Computed, Methods, Watchの使い分けをざっくりと
- 投稿日:2020-08-07T10:23:09+09:00
Vue.jsでcssのカスタムプロパティーを使って背景画像を動的に変更する
概要
vue.jsでコンポーネントを作成した際に、cssで指定する背景画像を指定したいことってあると思うんですが、その方法についてハマったので共有します。
作ったもの
※ compostionAPIを使っていますが、必要に合わせてお使いのものにしてください。
<template> <section :style="styles"> <h2>タイトル的な何か</h2> // ここで使う背景画像を動的に変更したいとする </section> </template> <script lang="ts"> import { defineComponent, PropType, computed } from '@vue/composition-api' type ContentType = 'hoge' | 'fuga' const iconUrl: { [key in ContentType]: NodeRequire } = { hoge: require('@/assets/images/hoge.png'), fuga: require('@/assets/images/fuga.png') } type Props = { contentType: ContentType } export default defineComponent({ name: 'custom-component', props: { contentType: { type: String as PropType<ContentType>, required: true } }, setup(props: Props) { const styles = computed(() => { return { '--url': `url(${iconUrl[props.contentType]})` } }) return { styles } } }) </script> <style lang="scss" scoped> section { h2 { background: var(--url) } } </style>説明
- Vue.jsでCSSのカスタムプロパティを動的に追加するにはコンポーネントに
<:style="style">
でデータを渡して上げることで使えます。
- こうすることで
root.element
の中にカスタムプロパティが登録されます。- 画像データのパスはそのまま渡しても、assetsのデータはバイナリで展開されるので、
require('画像パス')
で読み込んだ状態のものをurl()
に貼り付けた状態のものを渡して上げることでCSS内で動的に画像が変更されます。
- ここが結構トラップなので注意
使う時
<custom-component contentType="hoge"/> or <custom-component contentType="fuga"/>使うときは、
contentType
に指定したtypeを指定してあげるだけです。
※ ここのtypeはunionで指定していますが、コンパイルエラーにはならないけど...さいごに
画像が展開されたものになっていることを忘れていて、結構時間がかかってしまいました。
普段CSSをメインに触っていないので、url()
がどんな動作をしているのかとか理解が少なかったのでなれないことをやると時間かかりますね。
- 投稿日:2020-08-07T09:20:12+09:00
正式リリース前に総予習!! Vue3の変更点まとめ
8月上旬に正式リリース予定とされているVue3の変更点をいち早く理解できるように概要をまとめてみました。それぞれの項目ごとにvuejs/rfc又はVue3 Documentへのリンクを貼っているので索引的に使ってもらえると嬉しいです。
この記事は以下バージョン時点の情報です。
Composition APIの追加
おそらく一番大きな目玉となる変更はこちら。Composition APIという新しいAPIが追加されます(PluginでVue2系でも使用可能です)。
Composition APIはコンポーネントのロジックの柔軟な再構成を可能にする関数ベースのAPI群です。
なお、Vue2系のOptions APIのサポートも継続されるので、Vue2系からのVue3へのバージョンアップ時にComposition APIに書き換える必要はありません。<!-- Vue3 Composition API --> <template> <form> <input type="text" v-model="formState.name" /> <input type="text" v-model="formState.value" /> <p>{{ contentLength }}</p> <button @click="submit" type="submit">submit</button> </form> </template> <script lang="ts"> import { defineComponent, computed, ref, onMounted, reactive } from "vue"; export default defineComponent({ props: { maxContentLength: { type: String, required: true } }, setup(props, context) { // reactive data const formState = reactive({ name: '', content: '' }) // computed const contentLength = computed(() => `${formState.content.length} / ${props.maxContentLength}`) // emit const submit = () => context.emit('submit', formState) // lifecycle hook onMounted(() => { // .... }) return { formState, contentLength, submit }; } }); </script>Composition APIについてはこちらの記事でより詳細にまとめています。
先取りVue 3.x !! Composition API を試してみる - Qiita
Vue Composition APIで使えるリアクティブ関連のAPI一覧 - QiitaTeleportの追加
Teleportは定義したコンポーネントが属するDOMツリーとは別の場所に、まるでテレポートしたかのようにコンポーネントを移動できる機能です。Vue2系でもサードパーティのプラグインLinusBorg/portal-vueで実現されていました。
以下のようにモーダル表示をハンドルするコンポーネントで
<teleport>
を使うと、to
で指定したDOM要素に内部のコンポーネントを移動できます。<template> <div class="container"> <button @click="toggleModal">toggle modal</button> <teleport to="#teleport-target"> <MyModal v-if="isVisible"/> </teleport> </div> </template> <script lang="ts"> import { defineComponent, ref } from "vue"; export default defineComponent({ setup() { const isVisible = ref(false); const toggleModal = () => { isVisible.value = !isVisible.value; }; return { isVisible, toggleModal }; } }); </script>
to
で指定できるDOM要素は自身のVueインスタンスをマウントしたDOM要素以外も指定できます。<html lang="ja"> <body> <div id="app"></div> <!-- VueがマウントされるDOM --> <div id="teleport-target"></div> <!-- teleportで指定されているDOM。ここにMyModalが表示される--> <script src="/dist/main.js"></script> </body> </html>
teleport
を利用すると、いままでCSSのz-index
で調整していたDOMの重なり順制御を、宣言的に制御できるようになりz-index
の指定に悩まされることはなくなります。
Vue3を入れたらすぐに使いたいと思える実用的な機能ですね。こちらの記事でも詳細をまとめています。
Vue.js 3.0 の新機能を試す。 〜 Teleport 編〜 - QiitaFragmentsの追加
FragmentsはVue2系では実現できなかったmulti-root nodeのコンポーネントを可能にするものです。
Vue2系ではコンポーネントのroot要素は必ず1つという制約がありました。<template> <div> <header>...</header> <main>...</main> <footer>...</footer> </div> </template>Vue3ではFragmentsのおかげでその制約がなくなり、root要素に複数の要素を記載できます。冗長なdivでのラップなどが不要になります。簡潔で良いですね。
<template> <header>...</header> <main>...</main> <footer>...</footer> </template>ただ、注意点としてコンポーネントに直接Class指定をしていると、今まで自動的にroot要素にClassを当てられたのですが、それがfragmentsの場合は明示的な指定が必要になるというのがあります。
Suspenseの追加(実験的)
Suspenseは非同期処理が解決されるまでフォールバックコンテンツ(例えばLoading中アイコン)を表示してくれる特別なコンポーネントです。いままで、
v-if="loading === true"
などの状態変数を使って制御していたものを、状態変数を使わずに簡潔に書くことができます。Suspenseはまだ実験的な機能で、Vue3.0の段階では仕様が確定しておらず今後変更が入ることも考えられます。Vue 3.1での正式リリースを見越しているとのことです。
<template> <Suspense> <template #default> <AsyncComponents/> <!-- 非同期処理を実行するコンポーネント --> </template> <template #fallback> loading... </template> </Suspense> </template>Suspenseの詳細はこちらにまとめています。
Vue.js 3.0 の新機能を試す。 〜 Suspense 編〜 - Qiitav-modelの仕様変更
v-modelの機能が拡張され、ひとつの要素に複数のv-modelの設定が可能になります。
Vue2系のv-bind.syncで実現していたようなことがv-modelで実現できるようになるイメージですね。この機能によってv-bind.syncは代替されるので利用できなくなります。以下v-modelの複数バインドの例です。
v-model:xxxx
のxxxのところで明示的にバインドするプロパティを指定しています。<!-- 親コンポーネント --> <template> <UserForm v-model:name="name" v-model:message="message" /> </template><!-- UserFormコンポーネント --> <template> <form> <label>name</label> <input type="text" @input="$emit('update:name', $event.target.value)" > <label>message</label> <input type="text" @input="$emit('update:message', $event.target.value)" > </form> </template> <script lang="ts"> import { defineComponent } from "vue"; export default defineComponent({ props: { name: { type: String, default: '' }, age: { type: String, default: '' } }, setup() { return {} } }) </script>また、modifiersの機能が拡張され、
.lazy
,.number
,trim
のデフォルトのmodifiers以外に独自に,modifiersを定義できるようになりました。
以下はcapitalizeというmodifiersを設定している例です。v-model:name.capitalize=
とすることで入力値にcapitalizeを実行しています。<!-- 親コンポーネント --> <template> <form> <MyName v-model:name.capitalize="form.name"/> <!-- ... --> </form> </template><!-- MyNameコンポーネント --> <template> <input :value="name" @input="updateName" type="text" name="name" /> </template> <script> import { defineComponent } from "vue"; export default defineComponent({ props: { name: { type: String, default: '' }, nameModifiers: { default: () => ({}) } }, setup (props, { emit }) { const updateName = event => { let val = event.target.value if (props.nameModifiers.capitalize) { val = val.charAt(0).toUpperCase() + val.slice(1) } emit('update:name', val) } return { updateName } } } </script>scoped CSSの仕様変更
scoped CSSで使える擬似クラスの仕様変更・機能追加が行われます。
まず1つ目が::v-deep()
でこれは、今まで/deep/
として設定していた、子コンポーネントにスタイルを適用するための擬似クラスです。
/deep/
の記法がDEPRECATEDとなり、新たに::v-deep()
が追加されています。::v-deep(.foo) {}/* コンパイル後 */ [v-data-xxxxxxx] .foo {}2つ目が、
::v-slotted()
で、これはslotで受け取った親コンポーネントの要素にスタイルを適用するためのものです。Vue3からデフォルトではslotで受け取った要素は、子コンポーネントのスタイルが適用されなくなるため、こちらが追加されました。::v-slotted(.foo) {}/* コンパイル後 */ .foo[v-data-xxxxxxx-s] {}最後が、
::v-global()
で、これはscoped CSSの中でグローバルなスタイルの定義を可能とするものです。この擬似要素を設定したクラスには[v-data-xxxx]
が付与されないため、Scopedではなくグローバルに適応されます。::v-global(.foo) {}.foo {}$attrsの仕様変更
$attrs
の機能が拡張され、今まで$listeners
で参照していたネイティブイベントや別管理であったclass、styleも全て包括して$attrs
から取得できるようになりました。<MyButton @click="handleClick" @custom="handleCustom" v-model="value" type="button" class="btn" />// MyButton.vue $attrs: { class: 'btn' type: 'button', onClick: handleClick, onCustom: handleCustom, 'onUpdate:modelValue': () => { value = payload }, }そのため、
$listeners
はDEPRECATEDとなります。(実行時に警告がでます)
$listeners
を使って透過的なラッパーコンポーネントを作っていた場合は、$listeners
を削除し$attrs
への書き換えが必要です。<!-- Vue2系 --> <input v-bind="$attrs" v-on="$liseners" /><!-- Vue3 --> <input v-bind="$attrs" />Global APIの仕様変更
Vueの初期化処理が変わります。
createApp
でインスタンスを生成し、そのインスタンススコープでuse
やmixin
などが適応されるようになります。
これによってテストケースでのグローバル設定の汚染を防いだり、同一ファイル内で別設定のVueインスタンスの初期化が容易になります。import { createApp } from 'vue' import App from './App.vue' const app = createApp(App) // インスタンスを生成 app.config.isCustomElement = tag => tag.startsWith('app-') app.use(/* ... */) // インスタンススコープで設定される app.mixin(/* ... */) app.component(/* ... */) app.directive(/* ... */) app.config.globalProperties.customProperty = () => {} app.mount(App, '#app')filterの廃止
filterは、パイプ演算子
|
を使って関数適応をlinuxの標準入出力のパイプラインするものですがJavaScriptにはない独自構文で学習コストが増加する、構文解析を複雑にするという理由で削除されます。filterの処理は通常の関数適応で代替できます。
<!-- filter形式 --> <p>{{ msg | uppercase | reverse }}</p> <!-- 通常の関数適応形式 --> <p>{{ reverse(uppercase(msg)) }}</p>他の代替方法についてはrfcs/0015-remove-filters.md#alternativesをご覧ください。
Event Emitter系のAPIの廃止
Event Emitter系のAPI(
$on
,$off
,$once
)が削除されます。
グローバルなイベント管理としてEvent Emitterを仕様している場合は書き換えが必要です。
Event管理の代替にはMittなどの別のEvent Emitterライブラリの利用が推奨されています。Functional Componentの廃止
Functional Componentはdata等の状態を持たない代わりにレンダリングパフォーマンスの大幅な改善が行えるコンポーネントです。
Vue3ではFunctional Componentを作る際に使用するfunctional: true
のオプションや、<template functional>
が削除されます。Vue3では、通常のコンポーネントとFunctional Componentの性能差は大幅に縮小され、ほとんどのユースケースでは取るに足らないものとなったそうです。
SFC上でfunctionalを使用している場合は、functionalの属性を削除し、attrs、listnersの参照方法の修正が必要です。
<!-- Vue2 functional component --> <template functional> <component :is="`h${props.level}`" v-bind="attrs" v-on="listeners" /> </template> <script> export default { props: ['level'] } </script><!-- Vue3 --> <template> <!-- functionalを削除 --> <component v-bind:is="`h${props.level}`" v-bind="$attrs" <!-- attrs及びlistnersは$attrsにまとめられる --> /> </template> <script> export default { props: ['level'] } </script>終わりに
以上「正式リリース前に総予習!! Vue3の変更点まとめ」でした。
色々ワクワクするような新機能が追加されていてVue3のリリースが待ち通しいですね。
他にも諸々細かい点が変わってるのでより詳細な変更はvuejs/rfcをご確認ください。参考
- 投稿日:2020-08-07T08:12:59+09:00
Vue学習
created
— だーいし★/PHP/JS/???? (@issiyrun) August 6, 2020
初期に外部のページからデータ取得
APIからデータ取得→dataに格納
mounted
DOMが作られているため画面操作を行える。
beforeUpdate
再描画前に実行
updated
再描画後に実行
特定のタイミングで処理を挟み込める。#駆け出しエンジニアと繋がりたいVue.js
— だーいし★/PHP/JS/???? (@issiyrun) August 6, 2020
グローバル登録と
ローカル登録
グローバル登録
→どこからでも呼び出せるがメモリ食ったりする。
ローカル登録
→登録先コンポーネント内のテンプレートからしか呼び出せない。削除しても他のコンポーネントに影響がないのは便利。#駆け出しエンジニアと繋がりたいFireBase
— だーいし★/PHP/JS/???? (@issiyrun) August 6, 2020
Baas Backend as a Service
と呼ばれる部類のサービス
認証 DBの機能利用できる。
サーバレス増えて、バックエンドも値だけ投げたら加工してくれるAPIを提供する機能もどんどん出てきそう。。。
フロントにより力入れたくなる。。。#駆け出しエンジニアと繋がりたいFireBase便利そうやね。。
色んな展開の仕方があると思うけどAWSと同じで従量課金やから注意やね。