これを転載したもの
https://github.com/tac0x2a/vue_playground
Vue.js の基本
基本ルートのテンプレート
<body>
<div id="app">
<!-- ここに書く -->
</div>
</body>
var app = new Vue({
el: '#app',
...
})
app
はセレクタ(#app
)に対応。
アプリと配置する要素を紐付けることをマウントという。
var app
に代入してもしなくてもよい。
ディレクティブ
ディレクティブとは、 DOM 要素に対して何かを実行することをライブラリに伝達する、マークアップ中の特別なトークンです。
Vue.js でのディレクティブのコンセプトは徹底的にシンプルです。Vue.js のディレクティブは、下記のフォーマットのように、接頭辞のついた HTML 属性の形でのみ表されます。
基本は以下。
ディレクティブ
:引数
.修飾子
= 値
(ex) v-bind:value.sync = "message"
Text(v-text="式"
, {{式}}
)
<!-- dataオプションにmessageプロパティが登録されているとして -->
<!-- messageの値を表示 -->
<h1 v-text="message"></h1>
<!-- こう書く場合のほうが多そう -->
<h1>{{message}}</h1>
<!-- 42が表示される -->
<h1>{{40+2}}</h1>
繰り返し(v-for="ループっぽい式"
)
<!-- dataオプションにitemsプロパティがリストとして登録されているとして -->
<li v-for="i in items">{{i}}</li>
<li v-for="(item, index) in items">{{i}}</li>
<!-- 要素ごとに条件でclass設定できたりする -->
<li v-for="item in array" v-bind:class="{rainbow_badge: lv <= 50}" v-if="item != 'a'">
key
としてユニークな値をとるプロパティを指定することで、要素の識別と効率的な描画処理が可能になる。key
がないと、リストが更新された際に、リスト内の全要素を再描画してしまう。(変化した要素をvueが特定できないため。)
<li v-for="item in array" :key="item.id">
条件
式を評価した結果に応じて要素を消したりする。値の真偽値解釈はJavaScriptの条件式と同じ。
v-if="式"
, v-show="式"
<!-- dataオプションにshowプロパティが登録されているとして -->
<!-- DOMごと消える -->
<div v-if="show">v-if {{show}}</div>
<template v-if="show"></template>
<!-- DOMには残るが、style="display: none;" 属性が付与されるため、消えたように見える -->
<div v-show="show">v-show {{show}} </div>
-
v-if
はDOMごと消えてるため、配下の要素で参照するプロパティの有無を条件に入れる等でエラー回避したりできる。また、監視が解除されて破棄されるため、次に描画されるときは状態が初期化されてしまう。v-if
は単一な要素にしか付与で機内が、template
要素に付与することで、template
配下の要素をまとめて消したりできる。 -
v-show
は消えたように見えるだけ。切り替え頻度が高いものはこちらを使うのが良い。
v-else-if="式"
, v-else
いわゆるelseif
節やelse
節に相当。
データバインディング
属性のデータバインディング(v-bind:属性名="属性値の式"
)
-
<div key="id">
: "id" という文字列 -
<div v-bind:key="id">
:id
というJavaScriptの変数の値が展開される
以下はどちらで書いても同じ
<div v-bind:key="id">
<div :key="id">
直値で与えた属性と併用する例。
<!-- dataオプションのurlプロパティは展開される。 -->
<!-- 直値で与えた属性と同じ名前のプロパティを指定する場合、プロパティの値で上書きされる -->
<a v-bind:href="url" href="https://default.com" target="_blank">URL</a>
クラスとスタイルのデータバインディング(v-bind:class="{属性名: 条件式, ...}"
, v-bind:style="{属性名: 式, ...}"
)
オブジェクトや配列をバインドすると、クラス名やCSSプロパティとして展開される。
<p v-bind:class="{a: isA, b: isB, c: isC }">
<p v-bind:style="{color: textColor, backgroundColor: bgColor}">
var app = new Vue({
el: '#app',
data: {
isA: true,
isB: false,
isC: true,
textColor: 'blue',
bgColor: 'green',
}
})
これで動かすと
<p class="a c">
<p style="color: blue; backgroundColor: green;">
となる。
class
で指定する式は条件式として評価され、真となる値だけが残るようだ。
style
の方は値が文字列として展開されるようだ。
属性のデータバインディングと同じように、以下はどちらで書いても同じ。
<p v-bind:class={...}>
<p :class={...}>
オブジェクトデータを直接渡すこともできる。
<p v-bind:style="styleObject">
var app = new Vue({
el: '#app',
data: {
styleObject: {
color: 'blue',
backgroundColor: 'green'
}
}
})
まとめてバインディングする(v-bind="評価するとobjectになる式"
)
以下はどちらで書いても同じ。
<img v-bind:src="item.src" v-bind:alt="item.alt" v-bind:width="item.width" v-bind:height="item.height">
<img v-bind="item">
一部だけ書き換えることもできる。
<img v-bind="item" v-bind:width="item.width*2">
イベントハンドリング(v-on:click="評価するとfunctionになる式"
, v-on:click="式"
)
-
v-on:click
ボタンクリック時 -
v-on:load
画像などの読み込みが完了したとき -
v-on:scroll
要素のスクロールイベント -
v-on:mousewheel
マウスホイールイベント -
v-on:dragstart
ドラッグ -
v-on:input
inputに値が入力された
<button v-on:click="handleClick">Click</button>
<!-- 引数を与える場合は()で指定する。ループの中でインデックスを与えたりして使う -->
<button v-on:click="bracket(42)">Click</button>
var app = new Vue({
...
methods: {
handleClick: function (event) {
alert(event.target)
},
bracket: function (idx) {
// 配列の要素を置き換える場合は $set を使う必要がある。
// 代入すると配列要素が更新されただけになってしまうため、再描画されない。
// $set は値をリアクティブデータとして追加する。
this.$set(this.array, idx, "[" + this.array[idx] + "]")
}
}
})
以下はどちらで書いても同じ。
<button v-on:click="bracket(42)">Click</button>
<button @click="bracket(42)">Click</button>
もとのイベントはこんな感じで拾える。
<button v-on:click="handleClick($event)">Click</button>
var app = new Vue({
...
handleClick: function (event) {
alert(event.target)
},
...
})
コンポーネントはv-on
でイベントをハンドルできるが、window
やbody
についてはaddeventListener
メソッドで登録しておく必要がある。
フォーム入力バインディング(v-model="リアクティブデータの変数"
)
input 要素 や textarea 要素、 select 要素に双方向 (two-way) データバインディングを作成し、指定したリアクティブデータの変数に入力値がセットされる。
<input v-model="out" />
<input v-model="round" type="range" min="0" max="50" />
v-model
を使わずに、v-bind
やv-on
で頑張ることもできる。自分でバリデーションしたいときはこっち?
<input v-bind:value="out" v-on:input="handleInput" />
var app = new Vue({
...
methods: {
// 10文字未満に制限する
handleInput: function (event) {
if (event.target.value.length < 10) {
this.out = event.target.value;
}
},
}
...
})
いろいろ
<!-- 文字列 -->
<input v-model="out" />
<!-- 複数行であっても文字列 -->
<textarea v-model="out" />
<!-- v-model.numberとすることで、プロパティに数値として値を設定できる -->
<input type="range" v-model.number="r">
<!-- "#008000" みたいな文字列 -->
<input type="color" v-model="col">
<!-- ラジオボックスは文字列 -->
<input type="radio" value="フシギダネ" v-model="poke"> フシギダネ キミにきめた! </label>
<input type="radio" value="ヒトカゲ" v-model="poke"> ヒトカゲ キミにきめた! </label>
<input type="radio" value="ゼニガメ" v-model="poke"> ゼニガメ キミにきめた! </label>
<!-- 択一なselectは文字列 -->
<select v-model="out">
<option disabled="disabled">以下から選択!</option>
<option value="a"> A </option>
<option value="b"> B </option>
<option value="c"> C </option>
</select>
<!-- 複数選択のselectは配列 -->
<select v-model="out" multiple>
<option disabled="disabled">以下から選択!</option>
<option value="a"> A </option>
<option value="b"> B </option>
<option value="c"> C </option>
</select>
<!-- チェックボックスはBoolean。値を指定することもできる -->
<input v-model="out" type="checkbox" />
<input v-model="out" type="checkbox" true-value="真" false-value="偽" />
複数のチェックボックスは配列にもできる
<label><input v-model="array" type="checkbox" value="a"> A </label>
<label><input v-model="array" type="checkbox" value="b"> B </label>
<label><input v-model="array" type="checkbox" value="c"> C </label>
<!-- AとCにチェックを入れると ["a", "c"] になる -->
<p>{{array}}</p>
var app = new Vue({
...
data: {
array: []
}
...
})
修飾子についてはこちら
数値に変換したり(.number
)、余白を取り除いたり(.trim
)できる。
なお、画像などのtype="file"
はv-model
を使えないので、change
イベントをハンドルする。
<input type="file" v-on:change="handleChange">
算出プロパティ
計算をキャッシュしておいて、{{halfWidth}}
のように参照できる。
var app = new Vue({
computed: {
halfWidth: function(){
...
return width/2.0;
},
halfRect: function(){
return {
w: halfWidth,
h: width*0.6 / 2.0
}
}
}
})
functionの中でリアクティブデータを参照している場合、値が変更されると再計算される。
計算コストの高い処理結果をキャッシュするのに使える。例えばリストの絞り込み結果とか。
監視
var app = new Vue({
watch: {
監視対象のリアクティブデータやプロパティ: function(新しい値, 古い値){
...
},
}
})
フォームの入力を監視して、値が変更されたら検索APIコールして・・・みたいな使い方で動的検索ができる。
フィルタ
var app = new Vue({
filters: {
bracket: function(v){
return "[" + v + "]"
},
upper: function(v){
return v.toUpeerCase()
}
}
})
<p>{{ str | upper | bracket }}</p>
methodsとよく似ているが、文字列を加工するときは便利。
パイプみたいになっていて、左から右へデータが流れていく。
カスタムディレクティブ
v-
から始まるディレクティブを自分で作れるらしい。
コンポーネント
JavaScriptとテンプレート(HTML, CSS)を1つにまとめたもの。帰納単位に分離して開発する仕組み。
コンポーネントはWebにも転がっている
awesome-vue
https://github.com/vuejs/awesome-vue
Vue Curated
https://curated.vuejs.org/
element
https://github.com/ElemeFE/element
紹介記事: https://s8a.jp/vue-js-library-element
Onsen UI
https://ja.onsen.io/
モバイル向けのUIを簡単につくれる。良さそう。
コンポーネントを自作する
グローバルに登録
Vue.component('new-component', {
template: '<p>New Component!!</p>',
data: function(){
return {
...
}
},
methods: {
...
}
})
<div id="app">
<!-- どのVueテンプレートからでも参照できる -->
<new-component></new-component>
<!-- <p>New Component!!</p> として表示される -->
</div>
-
new Vue()
と同じように定義する。ただし、data
はObjectを返すfunctionになっていなければならない。 -
template
で指定するhtmlはツリーになってないとだめ。ルートは1つ。<div>
とかでくくっておくのがよい。 - コンポーネントはネストできる。
ローカルへの登録
var newComponent = {
template: '<p>New Component!!</p>'
data: function(){
return {
...
}
},
methods: {
...
}
}
var app = new Vue({
el: '#app',
components: {
//これで<new-components>が使用できるようになる
'new-components': newComponents
}
})
基本的にローカル登録にしておいてスコープを絞っといたほうがいい。
コンポーネント間の通信
ネストしたコンポーネントの親から子(props down)
静的に値を渡す。引数の値渡しみたいな感じ?
<!-- 親のテンプレート -->
<child name='Mario', age=10></child>
<child name='Luigi', age=8></child>
Vue.component('child', {
template: '<p>{{name}}({{age}})</p>',
props: ['name', 'age'] //ここで受け取る属性を指定する
// 型指定することもできる
// props: {
// name: String
// age: Number
// }
})
リアクティブデータを渡す場合
<child v-bind:name='Mario', age=10></child>
これで、親側でname
を変更すると、子のnameが変更される。
ネストしたコンポーネントの子から親(event up)
<!-- 親 -->
<child v-on:child-event="parentsHandler"></child>
// 子
this.$emit('child-event')
$emit('イベント名', 引数,...,)
を使う。子要素のボタンを押したときに、親要素をどうこうしたいときに。
親子で双方向のデータバインディングする(v-model
)
<!-- 親 -->
<child v-model:"date"></child>
<!-- 上記は以下の糖衣構文 -->
<child v-bind:value="date" v-on:input="date = $event"></child>
// 子から以下のように$emitすることで、dateが自動で更新される。
// v-modelで子へ渡されるプロパティはvalueなので、子側のpropsに指定するのを忘れないように!
this.$emit('input', '20210-04-28')
いくつもバインドする場合は .sync
修飾子を使う。
親子ではないコンポーネント間
var bus = new Vue() //イベントバス。
var bus = new Vue({
data: {
... //値をもたせることもできる。
}
})
というのを作っておいて
// 受け取る方
bus.$on('何らかの-event', function(){
...
})
// eventを発行する方
bus.$emit('何らかの-event')
スロット
親コンポーネントから、子コンポーネントのテンプレートの一部に何かを差し込むのに使う機能。
<!-- 子のテンプレート -->
<p>ここに入る→ <slot></slot><p>
<!-- 親 -->
<child>これを入れる</child>
<!-- するとこんな感じになる -->
<p>ここに入る→ これを入れる</p>
<slot>
タグが置き換わる。 <slot name=''>
とすることで複数のスロットを名前付きで挿入することができる。
ほかにもいろいろ使えるらしい。
Vue でアプリを作る
- Vuex: 状態管理用ライブラリ。複数コンポーネントでのデータ共有や、アプリの状態の一元管理を行う。
- Vue Router: 複数の画面とURLを紐付ける、SPA構築用ライブラリ
Vue CLI
アプリ構築用ツール郡のCLI。
かんたんにwebpackで単一ファイルコンポーネントを作ることができる。
内部ではBabel使ってるらしい。
プロジェクトを作る
$ vue create <プロジェクト名>
# 対話形式で色々設定する。 `vue ui` でブラウザから同様のプロジェクト作成を行える。
$ cd <プロジェクト名>
$ npm run serve
http://localhost:8080/ にアクセスすると、アプリ画面が表示される。
ビルド設定のカスタマイズは、プロジェクトルートに vue.config.js というファイル作成してそこに追加していきます。
単一ファイルコンポーネント(.vue
ファイル)
単一ファイルコンポーネント(SFC, Single File Components) として、 HTML/JavaScript/CSSをまとめた .vue
ファイル単位で管理する。
index.html
<div id="app"></div>
index.js
import Vue from "vue";
import App from "./Hello";
Vue.config.productionTip = false;
new Vue({
el: "#app",
template: "<App/>",
components: { App }
});
Hello.vue
<template>
<p>{{ greeting }} World!</p>
</template>
<script>
export default {
data: function() {
return {
greeting: "Hello"
}
}
};
</script>
<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>
-
<template>
: コンポーネントのテンプレート部分。必要なパッケージ入れてlang="jade"
とすると Jadeで書けたりする。 -
<script>
: コンポーネントのテンプレート以外の部分。 -
<style>
: CSSの部分。scoped
にすると、このコンポーネントとその配下のコンポーネントで有効になる。こちらもlang=stylus
でStylusで書けたりする。
Vuex
Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリ
あなたのアプリがシンプルであれば、Vuex なしで問題ないでしょう。
コンポーネント横断で使えるリアクティブなグローバル変数群?
src/store.js
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment: state => state.count++
}
})
export default store
import store from '@/store.js' // @はsrcのエイリアス
console.log(store.state.count) // -> 0
store.commit('mutation')
console.log(store.state.count) // -> 1
複数のコンポーネントで使う場合はnew Vue
するときに指定する
src/App.vue
import store from './store.js'
new Vue({
...
store,
...
})
適当なコンポーネント.vue
export default {
craeted() {
console.log(this.$store.state.count)
this.$store.commit('increment')
}
}
this.$state
でアクセスする。
Vue Router
コンポーネントとURLを紐付けてコンテンツを生成し、SPAを構築するための拡張ライブラリ。
Vue Router は Vue.js 公式ルータです。これは Vue.js のコアと深く深く統合されており、Vue.js でシングルページアプリケーションを構築します。機能は次の通りです:
・ネストされたルート/ビューマッピング
・モジュール式、コンポーネントベースのルータ構造
・ルートパラメータ、クエリ、ワイルドカード
・Vue.js の transition 機能による、transition エフェクトの表示
・細かいナビゲーションコントロール
・自動で付与される active CSS クラス
・HTML5 history モードまたは hash モードと IE9 の互換性
・カスタマイズ可能なスクロール動作
準備
$ npm install vue-router
使ってみる
慣例として、ルートに直接紐づくコンポーネントはviews
やpages
のようなディレクトリにまとめることが多い。
src/views/Home.vue
<template>
<h1>Home</h1>
</template>
src/views/Product.vue
<template>
<h1>Product</h1>
</template>
src/router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home.vue'
import Product from '@/views/Product.vue'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{ path: '/product', component: Product },
]
})
export default router
src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from '@/router.js'
Vue.config.productionTip = false
new Vue({
router: router,
render: h => h(App),
}).$mount('#app')
src/App.vue
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link>
<router-link to="/product">Product</router-link>
</nav>
<router-view />
</div>
</template>
これで、 http://localhost:8080 にアクセスすると、"Home" と "Product" リンクが生成されて、クリックするとそれぞれのコンポーネントが <router-view />
の位置に表示される。
ハッシュモードとヒストリーモード
vue-router のデフォルトは hash モード です - 完全な URL を hash を使ってシミュレートし、 URL が変更された時にページのリロードが起きません。
history モードを使用する時は、URL は "普通" に見えます e.g. http://oursite.com/user/id 。美しいですね!
ヒストリーモードを有効にするには、VueRouter
のmode
プロパティを'history'
にして、サーバの設定を変更する必要がある。
src/router.js
const router = new VueRouter({
mode: 'history',
routes: [...]
})
nginxの例
location / {
try_files $uri $uri/ /index.html;
}
その他サーバの設定方法はこちら参照。
ルート定義のオプション
...
const router = new VueRouter({
mode: 'history',
routes: [
{
name: 'prod',
path: '/product/:id', //URLからパラメータを受け取る場合はこう書く。
//path: '/product/:id(\\d+)', //正規表現で数字のみにマッチさせることもできる
compnent: Product,
meta: { requiresAuth: true } //認証が必要な場合はtrue
},
{
name: 'prodlist',
path: '/product', // パラメータなしの場合はrouteを別にすることもできる。
compnent: ProductList,
meta: { requiresAuth: true } //認証が必要な場合はtrue
}
],
// リダイレクトの設定。リダイレクト先はパスまたは名前で指定する
{ path: '/a', redirect: '/product' },
{ path: '/a', redirect: { name: 'prod'} },
})
受け取ったパラメータはこのように参照する。
this.$route.params.id //URLパラメータを受け取る
this.$route.query.id //クエリパラメータを受け取る場合はこう
$route
は マッチしたルートの情報が入ったオブジェクト。
ちなみに、パラメータは$route
ではなくpropsとして受け取っておいたほうがいい。ユニットテストがしやすいので。
src/Product.vue
<template>
<div>
<h1>Product</h1>
<div v-if="pid">Param: ID:{{pid}}</div>
<div v-if="qid">Query: ID:{{qid}}</div>
</div>
</template>
<script>
export default {
props: {pid: Number, qid: Number} //受け取るパラメータ
}
</script>
src/router.js
...
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{ path: '/product', component: Product },
{
path: '/product/:id(\\d+)',
component: Product,
props: r => ({ qid: r.query.id, pid: r.params.id}) //こんな感じで。
},
]
})
...
ナビゲーション
<template>
<div id="app">
<!-- 基本はこれ -->
<router-link to="/product"></router-link>
<!-- テンプレートリテラルを使う場合。込み入った記述をするとき便利 -->
<!-- =より右をJavaScriptの式として評価したい場合は `:to` を使うっぽい -->
<router-link :to="`/product/${ id }`"></router-link>
<!-- aタグ以外で囲みたい場合は`tag`で指定する -->
<router-link to="/product" tag="button"></router-link>
<!-- objectで指定できる。パラメータを渡す場合に便利か -->
<!-- paramsを渡す場合は、routerにnameを登録しておく必要がある -->
<router-link :to="{ path: '/product' }"></router-link>
<router-link :to="{ path: '/product', query: { id: 42 } }"></router-link>
<router-link :to="{ name: 'prod', params: { id: 42 } }"></router-link>
</div>
</template>
アクティブなルートにマッチする router-link
には .router-link-exact-active
や .router-link-active
が付与されるので、簡単にハイライトできる。
.router-link-exact-active {background: #ff00ff} //完全一致したルート
.router-link-active {background: #ff00ff} //マッチしたパスを含むルート
コードから遷移するには $router
に対していろいろやる
this.$router.push('/product') // push, replace, go
this.$router.push({name: 'prod', params: { id: 1}}) //router-linkのようにパラメータを渡すこともできる
動的ルートのサンプル
ポケモン図鑑(Pokedex)を作る。データは src/DummyPokedex.js
にハードコードしたダミーを使う。
src/App.vue
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link>
<router-link to="/pokemon">Pokedex</router-link>
</nav>
<img alt="Vue logo" src="./assets/logo.png" />
<p>
<router-view />
</p>
</div>
</template>
<script>
export default {
name: "App"
}
</script>
src/api/DummyPokedex.js
const pokedex = [
{ number: 1, name: "フシギダネ", types: ["くさ", "どく"], height: 0.7, weight: 6.9 },
{ number: 4, name: "ヒトカゲ", types: ["ほのお"], height: 0.6, weight: 8.5 },
{ number: 7, name: "ゼニガメ", types: ["みず"], height: 0.5, weight: 9.0},
]
//ダミーデータとアクセス用のAPIを定義する
export default {
fetchAll() { return pokedex },
findFirst(number) { return pokedex.find(p => p.number == number) },
// DB等から読み込んでるように振る舞わせるため、`setTimeout` で2秒後にコールバックする
asyncFindFirst(number, callback) {
setTimeout(() => {
callback(this.findFirst(number))
}, 2000) // 2000ms 後に callbackする
}
}
src/router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home.vue'
import Pokedex from '@/views/Pokedex.vue'
import Pokemon from '@/views/Pokemon.vue'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{ path: '/pokemon', component: Pokedex },
{
path: '/pokemon/:number(\\d+)',
component: Pokemon,
props: route => ({
number: Number(route.params.number)
})
},
]
})
export default router
src/views/Pokedex.vue
<template>
<div>
<h1>Pokedex</h1>
<ul>
<!-- ポケモン一覧をを表示する -->
<li v-for="{number, name} in pokedex" :key="number">
<router-link :to="`/pokemon/${number}`">No.{{number}} {{name}}</router-link>
</li>
</ul>
</div>
</template>
<script>
import Pokedex from '@/api/DummyPokedex.js'
export default {
computed: {
pokedex: () => Pokedex.fetchAll()
}
}
</script>
src/views/Pokemon.vue
<template>
<div>
<div v-if="pokemon">
<h1>No. {{pokemon.number}} {{pokemon.name}}</h1>
<h2>体長:{{pokemon.height}}[m] 重さ:{{pokemon.weight}}[kg]</h2>
<h2>タイプ</h2>
<p v-for="type in pokemon.types" :key="type">{{type}}</p>
</div>
<div v-else>
<h2>Loading...</h2>
</div>
</div>
</template>
<script>
import Pokedex from '@/api/DummyPokedex.js'
export default {
props: {number: Number},
data(){
return {pokemon: null}
},
// computed では this.numberを参照できないので、watch で numberを監視するように。
watch: {
number: {
handler(){
// 遅延読み込みした結果を格納する
Pokedex.asyncFindFirst(this.number, pokemon => {
this.pokemon = pokemon
})
}, immediate: true //ここがtrueじゃないと、すぐにhandlerが呼ばれない。
}
}
}
</script>
src/views/Pokemon.vue
の下に子コンポーネントを作る場合は、src/views/Pokemon/*.vue
とするのがいいらしい。
遷移アニメーション
<transition name="viewwww">
<router-view />
</transition>
...
<style>
.viewwww-enter-active {
transition: opacity 0.5s;
}
.viewwww-enter, .viewwww-leave-to {
opacity: 0;
}
</style>
name
に指定したviewwww
が一致してないといけない。
ナビゲーションガード
遷移を操作するためのフック。
-
to()
: 遷移後のrouteオブジェクトを受け取る -
from()
: 遷移前のrouteオブジェクトを受け取る -
next()
: これを呼ぶとフックが解決してrouteの遷移が行われる。遷移を中止する場合はnext(false)
をコールする。next('/pokemon')
などで任意のrouteにリダイレクトもできる。
const router = new VueRouter({
routes: [
...
{
path: '/pokemon',
component: Pokedex
beforeEnter(to, from, next){ //このrouteへ遷移する前に呼ばれる。
next() //遷移してもOKな場合に呼ぶ
}
},
...
]
})
//グローバルなガードも使える
router.beforEeach((to, from, next) => {
//コンポーネントガード解決前に呼ばれる
...
})
router.afterResolve((to, from, next) => {
//コンポーネントガード解決後に呼ばれる
...
});
router.afterEeach((to, from) => { // 遷移が終わってから呼ばれるのでnextは使えない。
//すべてのルートの遷移後に呼ばれる
...
});
<script>
export default {
// beforeRoute{Enter, Update, Leave} が使える
beforeRouteEnter(to,from,next){
// この時点ではコンポーネントのインスタンスはまだ作られていないので、thisにアクセスできないことに注意。
next(vm => {
// vm には VueComponent が入っている
// このコールバックでthisにアクセスできる。
})
}
}
</script>
beforEeach
とかで、認証認可のチェックができる?
ES2015(ES6)関連
文法いろいろ
// 変数宣言
// var x = 0 //スコープ広い
let x = 0 //スコープ内でのみ有効
const y = 42 //再代入不可
// function
new Vue({
methods: {
// 以下は handle: function(){ ... } と同じ。
// プロパティとして関数を宣言するときは function を省略できる。
handle(){ ... }
}
})
//アロー関数
let hoge = () => "hoge"
let fuga = arg => "fuga and " + arg
let piyo = (arg1, arg2) => {
let joined = arg1 + "piyo" + arg2;
return joined;
}
//テンプレートリテラル
let template = `
<div class="sample">
<p>${ this.variable }</p>
</div>`
// オブジェクトプロパティのショートバンド
let short = {
hoge, fuga // 変数名がプロパティの名前になる
}
// let short = {
// hoge: hoge,
// fuga: fuga
// }
// 多重代入(分割代入)
let [a,b] = [1,2] // aに1, bに2が代入される
let { a } = {a:'A', b:'B', c:'C'} // a に 'A'が 代入される
// スプレッド演算子
let args = [1,2]
piyo(...args) // piyo(1,2)と同義
let args_ex = [ ...args, 3] // [1,2,3]
モジュール定義とインポート
Sample.js
var state = {
count: 1
}
export default state
export default state
は、デフォルトのimport文で呼ばれたときに返すデータを定義している。
import Sample from './Sample.js'
みたいに書くと、stateのオブジェクトをSampleという変数名としてインポートする。
export default
vs module.exports =
・基本的にはexport defaultを使う
・複数のモジュールをexportするときはexportを使う
・受け取り側はimportを使う
require
vs import
モジュール読み込みの仕様の主要なものとして、ESM (ECMAScript Modules)とCJS (CommonJS Modules)があります。
importを使うのがESM方式で、requireを使うのがCJS方式
require文は、CommonJSの仕様で、Nodejsの環境で動作してくれる書き方です。Nodejs環境ということはつまり、サーバサイドでの実行ということになります。
import を使うのが良さそう。
メモ
開発環境
Ubuntu(WSL)環境にNode.jsをインストールする
$ sudo apt update
$ sudo apt install nodejs npm
$ sudo npm install n -g
$ sudo n stable
$ # sudo apt purge -y nodejs npm #古いのは消すex
Vue CLI
$ sudo npm install -g @vue/cli
$ vue --version
@vue/cli 4.3.1
VS Codeに入れた拡張
いろいろ
-
<ol>
: 連番 -
<ul>
:・
だけ。