18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue.js+Nuxt.js+TypeScript+Pug+Sass

Last updated at Posted at 2018-12-24

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の違いがあまりわかってないので、時間があるときにそれぞれの機能をまとめていきたい。

目標

  • 各用語の意味がわかる
  • JsvaScript、ES2015(ES6)以降の基本構文がわかる
  • Vue.js、Nuxt.js、TypeScriptの基本構文がわかる
  • ライフスタイルにそった記載ができる
  • Vue.jsとNuxt.jsでどちらの機能を使用しているかわかる
  • APIで受け取ったデータと連携できる
  • VueXで状態管理が行える
  • Vue RouterでSPAのページを作成できる

参考

ディレクティブ

  • 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

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以降

参考:Vueのライフサイクルを完全に理解した

mounted

elementへのマウントがされた後に実行される。
mountedでは、子コンポーネント全てをマウントしたことは保証しない。
全てのコンポーネントがマウントされるまで、ロードを待つには以下のように記載。

要素の高さや幅を取りたいときに使うかな。

mounted.vue
<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と多分だいたい同じ

vue-property-decoratorを使うと次のように展開される。
展開後が本来のVue.jsの書き方なので、認識しておく。
デコレーターが便利すぎて、元のvue.jsの書き方を忘れてしまう(笑)

展開前.vue
<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>
展開後.vue
<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にはアクセス不可
Parent.vue
// 呼ぶ方が親コンポーネント
<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>
Child.vue
// 呼ばれる方が子コンポーネント
<template lang="pug">
.child-box
 p.text 子テンプレート
</template>

ref

子コンポーネントのDOMを直接操作する。

slot

子コンポーネントにテンプレートを挿入。

1分でわかるVue.jsの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)

$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:引数を使用できる。
  • @mixinで定義して、@includeで呼び出す。 => scssとsassで記載方法が違うのでハマった。
@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] ]

18
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?