Vue.js、Nuxt.js、TypeScript、Pug、Sassを案件で使用したので、覚え書き。
⭐️Vue.js
Vue.jsの特徴と概要。
- JavaScriptのフレームワーク
- MVVM(Model(ロジック)、View(見た目)、ViewModel(データのやり取りの橋渡し部分))という設計パターンを採用
- 双方向データバインディング(データの変更→UIの表示を更新、UIの変更→データの更新を自動的に行う機能)
- ディレクティブ
- コンポーネント指向
- 学習コストが低い
必要な知識
Vue.jsをはじめるにあたって、あらかじめ学習が必要な知識をまとめた。
Nuxt.jsやTypeScriptとの相性が良い。
個人的に、ES2015(ES6)以降の構文理解は必須。
vue-property-decorator(Nuxt.jsはnuxt-property-decorator)を使用する場合は、デコレーターの書き方をマスターしておく。
また、最近Vue.jsをはじめとするJSのフレームワークとTypeScriptをセットで使用されることが多いので、TypeScriptの型や構文の理解も必要。
Vue.jsとNuxt.jsの違いがあまりわかってないので、時間があるときにそれぞれの機能をまとめていきたい。
- HTML
- Pug
- CSS
- Saas or Scss
- JavaScript
- JavaScript基本構文
- ⭐️ES2015(ES6)以降(必須)
- ⭐️Nuxt.js
- ⭐️TypeScript(必須)
目標
- 各用語の意味がわかる
- JsvaScript、ES2015(ES6)以降の基本構文がわかる
- Vue.js、Nuxt.js、TypeScriptの基本構文がわかる
- ライフスタイルにそった記載ができる
- Vue.jsとNuxt.jsでどちらの機能を使用しているかわかる
- APIで受け取ったデータと連携できる
- VueXで状態管理が行える
- Vue RouterでSPAのページを作成できる
参考
- やわらかVue.js - 迷わないVueの学び方
- Vue.jsを100時間勉強して分かったこと
- はじめてのvue-property-decorator
- 2019年版Vue.jsを使ってる人には必ず知っていてほしいVue.jsの武器とドキュメントに書かれていないコンポーネントやメンテナンスの際に役立つTips
- Vue.js + Vuexでデータが循環する全体像を図解してみた
ディレクティブ
-
v-
からはじまるvue.js特有の属性 -
単一のJavaScript式
として認識される- 複数式は不可なので注意
- 逆に
v-
がついてないものは数値や文字列として認識
🙆♂️ <p v-show="hoge && fuga"></p>
🙅♂️ <p v-show="hoge === 1; onChange()"</p>
{{テキスト}}
{{}}
で囲むことで、Vueのプロパティの値を呼び出せる。
<p>{{ message }}</p>
v-bind
HTML要素を動的
に指定したい場合は、v-bindを使用
v-bind:
は:
に省略できる
<p v-bind:属性名="データを展開した属性値"></p>
<p v-bind:href="link"></p>
// 省略
<p :href="link"></p>
v-for
要素を繰り返し描画したい場合は、v-forを使用
<li v-for="item in list">{{ item }}</li>
v-on
イベントハンドリング
式が長くなったり、複数になった場合は関数化するといい
v-on
は@
に省略できる
<p v-on:イベント名="式として実行したい属性値"></p>
<p v-on:click="alert('hoge')"></p>
// 省略
<p @click="alert('hoge')"></p>
v-show
trueの場合に表示。
falseの場合は、display: none;
となるので、HTMLコードは残る
。
<p v-show="isShow">ほげ</p>
v-if
trueの場合に表示。
v-if、v-else-if、v-elseは同列に記載。
falseの場合は、HTML要素とその子要素をDOMから取り除く
ので注意。
templateタグを使ってグループ化ができる。v-showではできない
ので注意。
<template>
<p v-if="status === hogeA">Aパターン</p>
<p v-else-if="status === hogeB">Bパターン</p>
<p v-else>それ以外</p>
</template>
v-style & v-class
trueならstyle、classを付与。
クラス名にハイフンを含む場合は、''
で囲む。
<p :style="{ height: `${headerHeight}px` }">スタイル</p>
<p :class="{ child: isChild, 'is-active': isActive }">クラス</p>
複数のデータ
<script>
new Vue({
el: '#app',
data: {
item: {
id: 001,
src: 'item001.jpg',
alt: '商品画像1',
width: 200,
height: 200
}
}
})
</script>
プロパティを全てバインドしようとすると冗長になるので、オブジェクト
を渡して必要な要素にのみ変更を加える。
🙅♂️<img :src="item.src" :alt="item.alt ...">
🙆♂️<img :item :id="'thumbnail' + item.id">
v-model
- Vue.js:v-modelと$emitを使ってデータを読み書きする子コンポーネントをつくる
- フォーム入力バインディング
- 双方向データバインディング
1行で済んでしまうが、コンポーネントが親と子の関係の時に使用できる。3世代の場合は使用できないので注意。
<p v-on:input="item.name = $event.target.value"
v-bind:value="item.name"></p>
↓
<p v-model="item.name"></p>
ライフサイクル
ライフサイクルの流れ。
- インスタンス作成時
- beforeCreate
- Created
- マウント時
- beforeMount
- mounted ここでDOMにアクセスできる
- データ更新時
- beforeUpdate
- updated
- インスタンスの削除時
- beforeDestroy
- Destroyed
- エラー時
- errorCaptured ver2.5.0以降
mounted
elementへのマウントがされた後に実行される。
mountedでは、子コンポーネント全てをマウントしたことは保証しない。
全てのコンポーネントがマウントされるまで、ロードを待つには以下のように記載。
要素の高さや幅を取りたいときに使うかな。
<script>
mounted() {
this.$nextTick(function () {
// ビュー全体がレンダリングされた後にのみ実行される
this.onResize();
this.setContentHeight();
this.setBannerWidth();
})
}
</script>
Vue CLI
Vue.jsでアプリ開発する上で、利用するテンプレートを提供してくれるツール。
⭐️vue-property-decorator
Vue.jsをTypeScript特有のクラス構文で書くためのツール
Nuxt.jsを使用する場合は、nuxt-property-decoratorを使用
→vue-property-decoratorと多分だいたい同じ
- TypeScriptでVue.jsを書く
- vue-property-decorator
- 公式
- 参考
- nuxt-property-decorator
- vuex-class
vue-property-decorator
を使うと次のように展開される。
展開後が本来のVue.jsの書き方なので、認識しておく。
デコレーターが便利すぎて、元のvue.jsの書き方を忘れてしまう(笑)
<script>
import { Vue, Component, Prop } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Prop(Number)propA!: number
@Prop({ default: 'default value' }) propB!: string
@Prop([String, Boolean]) propC!: string | boolean
}
</script>
<script>
export default {
props: {
propA: {
type: Number
},
propB: {
default: 'default value'
},
propC: {
type: [String, Boolean]
},
}
}
</script>
その他
真偽値の受け渡し
- 値のないプロパティは
true
になる→trueは省略できる
🙆♂️ <blog-post is-published></blog-post>
🙅♂️ <blog-post is-published="true"></blog-post>
コンポーネント
- コンポーネント化することでメンテナンス性を向上させ、改修工数を削減→同じ機能は各ページに記載せずに1つのコンポーネントにまとめて使い回す
- 状態管理はコンポーネントには持たせず、ページに持たせる
- 複雑な状態管理になったらVuexを使用
- Prop down(親から子への値の受け渡し)と Events up(子から親へイベントの伝播)のバケツリレーが肝
<script>
prop
@Prop({ type: Boolean, required: true })
public hoge: boolean;
$emit
@hoge="$emit('hoge')
</script>
単一ファイルコンポーネント
- vue.jsのコンポーネントを単独のファイルとして作成する機能
- .vue拡張子を使用
-
<template>、<script>、<style>
のブロックで構成 - HTMLベースの構文
<template>
<p>{{ greeting }} World!</p>
</template>
<script>
module.exports = {
data: function () {
return {
greeting: 'Hello'
}
}
}
</script>
<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>
プリプロセッサ
- プリプロセッサとは独自の言語(メタ言語)で書かれた構文をcssやJavaScriptに変換してくれるもの
- sass、pug、typescriptなどをlangに指定するだけで、単一ファイルコンポーネント内で使えるようになる
<template lang="pug">
.hoge-box
p.text ほげほげ
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component({
components: {}
})
export default class HogePage extends Vue {}
</script>
<style lang="sass">
.hoge-box
&
width: 200px
& > .text
font-size: 14px
</style>
scoped css
- styleタグに
scoped
を付与すると、その単一ファイルコンポーネントのみに適用するカプセル化ができる。
<style lang="sass" scoped>
.hoge-box
&
color: red;
</style>
- 🙆♂️ 影響範囲やclass名などをあまり気にせずに書ける
- 🚨 単一ファイルの
<template>
内に記載されていないclass名にはアクセスできないので注意→子コンポーネントのcssにはアクセス不可
// 呼ぶ方が親コンポーネント
<template lang="pug">
.parent-box
Child.hoge-box
// Childコンポーネント呼び出し
// 親コンポーネントからアクセスできるように.hoge-boxを付与
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import Child from '@/components/Child.vue';
@Component({
components: {
Child
}
})
export default class Parent extends Vue {
// ここにParentコンポーネントのJavaScriptを記載
}
</script>
<style lang="sass" scoped>
/* 🙆♂️ Parent.vueファイル内のclassなので、アクセス可能 */
.hoge-box
&
color: red
/* 🙅♂️ Parent.vueファイル外のclass(Child.vue内のclass)なので、アクセス不可 */
.child-box
&
color: red
</style>
// 呼ばれる方が子コンポーネント
<template lang="pug">
.child-box
p.text 子テンプレート
</template>
ref
子コンポーネントのDOMを直接操作する。
slot
子コンポーネントにテンプレートを挿入。
slot-scope
is
動的にコンポーネントを切り替える。
⭐️Vuex
Vuexとは複雑な状態管理を行えるライブラリ。
簡単な状態管理であれば、Prop down(親から子への値の受け渡し)と Events up(子から親へイベントの伝播)のバケツリレーで行えるが、複雑になると管理しきれなくなる。
※状態とはデータのこと
用語
Actions
Mutations
State:状態
コンポーネント
参考
2018年Vue.jsとVuexを使ってる人には必ず知っていてほしいドキュメントに書かれていないコンポーネントやストア、メンテナンスの際に役立つTips
SPA
- シングルページアプリケーション(SPA)とは、単一のウェブページを使用して、要素の内容のみを置き換えて画面遷移させるアプリケーション設計のこと。
- 画面の更新に必要なもののみを読み込むため、描画が早いのが特徴。
- 従来ではページ全体を読み込み、再描画していたので、動作が遅かった。
⭐️Vue Router
SPA構築のための拡張ライブラリ。
ルーティング(ページ遷移など)の管理を行う。
参考
Vue.js+TypeScriptで開発するときの参考記事まとめ
⭐️Nuxt.js
- vue.js用のフレームワーク => .vueで記載
- SSR(サーバーサイドレンダリング)
- SSR以外でのnuxtによる恩恵が実感できていない
⭐️TypeScript
参考
型
- 型は今まで意識してこなかったが、これからは意識して記載したい
-
==
と===
では返り値が異なるので注意 - 型を厳密に管理することでコンパイルがエラーを吐いてくれるので、エラーの原因を突き止めやすい
- undefined と null と void の違い(イメージ)
// null 空 空白
const hoge = ""; // null => 箱はあるけど、中身が入ってない
// undefined 未定義
const hoge; // undefined => 箱すらない、中身に何も入ってない(代入されていない)
// void 無/初期値がない
function hoge(): void { => 関数の返り値として何もないことを表す
console.log('hello');
}
- null と undefined は厳密には異なるものなので注意
null == undefined // true
null === undefined // false
- 型推論 => 初期値がある場合は型は不要
- 早期returnは void
property:?
省略可能なプロパティ
プロパティの後ろに ?
をつけると省略可能
interface MyObj {
foo: string;
bar?: number;
}
MyObj.bar; // number | undefined
ref
TypeScriptのthis.$refs.ref
は、Vue | Element | Vue[] | Element[]
なので、instanceof
で型の判定が必要。
要素や要素の高さを取得したい場合によく使われるので、TypeScriptを使う場合は注意。
<script>
const { childComponent } = this.$refs;
// Vue Component
if (childComponent instanceof Vue) {
this.childHeight = childComponent.$el.clientHeight;
}
// HTML Element
if (childComponent instanceof Element) {
this.childHeight = childComponent.clientHeight;
}
</script>
ジェネリック
JavaScript
参考
JavaScriptやプログラミング全般でためになりそう、参考になりそうな記事。
基本
ES2015(ES6)
分割代入
const foods = {a: 'パン', b: 'ご飯', c: '麺'};
const {a, b, c} = foods; // プロパティ名と同じにする
const {a: a, b: b, c: c} = foods; // 省略
console.log(a); // 'パン'
配列操作
配列操作は、元のデータを変更せずに、新しい器(配列)を用意して作成。
ミューテートしてしまうと、意図しない結果になってしまう。
※ミューテート:既存のデータを変更すること
map
処理をして、新しい配列をつくる。
var images = [
{ height: '34px', width: '39px' },
{ height: '54px', width: '19px' },
{ height: '83px', width: '75px' },
];
var heights = images.map(function(image){
return image.height;
});
heights; // ["34px","54px","83px"]
filter
条件に合うものを絞り込んで、配列にする。
var post = { id: 2, title: 'タイトル'};
var comments = [
{ postId: 1, content: '記事1' },
{ postId: 2, content: '記事2' },
{ postId: 3, content: '記事3' },
{ postId: 2, content: '記事2' }
]
function commentForPost(post, comments) {
return comments.filter(function(comment){
return comment.postId === post.id;
});
}
commentForPost(post, comments); // [{"postId":2,"content":"記事2"},{"postId":2,"content":"記事2"}]
find
条件に合う 最初の要素
のみ返す。それ以降は、実行されない。
every, some
every:全て
の条件を満たしていれば、true。
some:1つでも
条件を満たすものがあれば、true。
reduce, reduceRight
処理をして一つの値を取得。
previousValueはそれまでの結果, currentValueは今の結果。
reduceRightは右からの処理。
forEach
⭐️HTML & CSS
Pug
HTMLのテンプレートエンジン。
HTMLタグ(開始と終了タグ)を省略できるので、慣れてしまうともう手放せなくなる。
Sass(Scss)
- これからはcssはSassで書こう。
- CSSのメタ言語Sass(SCSS)
- 変数に定義しておけば、変数を変更すれば全て変更される。ブレイクポイントなども変数に入れておくと良い。
$btn-white: #000
- @import:ファイル分割ができる
//ファイルの拡張子を省略できる
@import "button.sass";
@import "button";
- @extend:使い回すclassを継承することができる。
// ベースのボタン
.btn
padding: 10px 20px
border: 1px solid blue
border-radius: 4px
background-color: #fff
color: #000
// .btnのスタイルを継承
.btn-submit
@extend .btn
// スタイルの上書き
background-color: red
@mixin cassette($color: black , $width , $height) // scss
=cassette($color: black , $width , $height) // sass
border: 1px dotted $color
width: $width
height: $height
#saeach-cassette
@include box(blue , 300px , 600px) // scss
+box(blue , 300px , 600px) // sass
RSCSS
⭐️勉強
- Vue.js
- v-for="(item, key, index)" のkeyのあるなし
- トランジション&アニメーション
- 配列操作 slice、pushなど
- slot
- ライフサイクル
- virtualDOMとinputの字数制限注意(input自体に字数制限をかける、再レンダリング時に切り替わるので)
- Vuex
- 子、親、先祖など3ファイル以上のコンポーネント間での値の受け渡し
- v-modelの使い方、書き換え
- .sync
-
$emit
と@Prop
- TypeScriptと構文
- デコレーター(プロパティデコレータ)
- instanceof
- ES2015
- その他
- nullチェック => nullの可能性のあるDOMはnullチェック
memo
コンポーネントが増えても大丈夫。それぞれの関係。
Vue.js:部品
Vuex:ページ(状態管理)
Vue Router:たくさんのページを管理(SPAで必要な箇所のみ更新)
Object.keys
Object.values
Object.entries
let obj = { a: 1, b: 2, c: 3 }
console.log( Object.keys( obj ) ) // [ "a", "b", "c" ]
console.log( Object.values( obj ) ) // [ 1, 2, 3 ]
console.log( Object.entries( obj ) ) // [ ["a", 1], ["b", 2], ["c", 3] ]