Nuxt.js チュートリアル(todo app)
つくる物
https://silly-wilson-2f221d.netlify.app/
対象者
html、css、javascriptは少しわかる
vueの基本を調べてみて、何か簡単なアプリ作ってみたい人
学べること
Nuxtの基礎
Vuexの基礎
element-uiの基礎
Netlifyへのデプロイ方法
yarnを使いますので、npmでされる方は適宜コマンドを置き換えてください
セットアップ
yarn create nuxt-app nuxt-todo
nuxt-todoの箇所が作成されるディレクトリ名になります
お好きな名前を入れていただいて大丈夫です
上記のコマンドを打った後に、いくつか質問されます
今回は下画像のようにしました
Project name、Project description、Author nameは任意の物を入力していただいて大丈夫です
Choose the package managerはyarnでもnpmでもどちらでも構いません
他の項目は画像のものに合わせてください
アプリを起動する
cd nuxt-todo
yarn dev
アプリがたちがったらブラウザでlocalhost:3000にアクセスすると、下画像のようなデフォルト画面が表示されるはずです
NuxtでScssを使えるようにする
参考: https://ja.nuxtjs.org/api/configuration-css/#__layout
Nuxtはデフォルトでscssで使えません。
scssを使える方が便利なので、scssを使えるようにします
ターミナル(コマンドプロンプト)を開いてください
プロジェクト直下で下コマンドを叩いてください
yarn add -D node-sass sass-loader
これでscssが使えるようになります
Netlifyのためのconfig修正
後々必要になるので、プロジェクト直下にあるnuxt.config.jsにgenerate: { fallback: true }を追加してください
Nuxtのディレクトリ構造
参考 https://ja.nuxtjs.org/guide/directory-structure
今回はpages、store、layoutsディレクトリのみに手を加えるので、これらの解説をします
見た目(html、cssなどでいじるところ)はpages、layoutsです
layoutsがレイアウトというだけあって見た目の骨組みで、このlayoutsの中でpagesが表示されると思っていただければ大丈夫です(詳しくは公式を参照ください)
layoutsから修正します
layoutsディレクトリ直下のdefault.vueを以下のように書き換えてください
<template>
<div>
<el-container>
<el-header height="60px">
TODO APP
</el-header>
<nuxt />
</el-container>
</div>
</template>
<style>
html {
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, 'Helvetica Neue', Arial, sans-serif;
font-size: 1.2rem;
word-spacing: 1px;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: border-box;
margin: 0;
}
.el-header {
background-color: #2cb696;
display: flex;
align-items: center;
color: white;
}
</style>
layouts/default.vueの解説
vueの復習
vueファイルは3つの部分から構成されます
templateタグ -> htmlを書く場所
script -> JavaScriptやVueでロジックを書く場所
style -> css、scssを書く場所
templateタグにel-から始まるタグがありますが、これはelement-uiのコンポーネントを示しています
ここではel-containerとel-headerを使っていますが、これらはページのレイアウトを調整してくれます (詳しくは公式を参照)
構造としては下画像のようになります
el-containerの中にある<nuxt />タグの箇所に、ページ(上画像の青枠2つの部分)が自動入ります。この部分をpagesディレクトリ直下のファイルに記述します
書き換えると下画像のように表示されるはずです
lintエラーに対応する
実装中に画面にいきなり赤字でErrorが表示されることがあるかもしれません
これはeslintというコーディング規約に則っていないために発生します
この時は一つ一つコード修正でもいいのですが、ターミナル上で下コマンドを打てば自動で修正されるので便利です
yarn lint --fix
pages/index.vue
下のように記述してください
<template>
<el-container>
<el-aside width="400px">
<el-form
ref="form"
:rules="rules"
:model="form"
label-width="120px"
@submit.native.prevent="handleAdd"
>
<el-form-item prop="input">
<el-input
v-model.trim="form.input"
placeholder="todoを入力"
clearable
/>
</el-form-item>
</el-form>
</el-aside>
<el-main>
<el-row>
<el-col :span="8">
<ul class="list">
<li v-for="(todo, index) in todoList" :key="index" class="todo">
<span class="title" :class="todo.isChecked && 'checked'">{{
todo.title
}}</span>
<div class="icons">
<i class="el-icon-check" @click="handleCheck(index)"></i>
<i class="el-icon-delete" @click="handleDelete(index)"></i>
</div>
</li>
</ul>
</el-col>
</el-row>
</el-main>
</el-container>
</template>
<script>
export default {
data() {
return {
form: {
input: ''
},
todoList: [],
rules: {
input: [
{ required: true, message: '何か入力してください', trigger: 'change' }
]
}
}
},
methods: {
handleAdd() {
this.todoList.push({
title: this.form.input,
isChecked: false
})
this.$refs.form.resetFields()
},
handleCheck(index) {
this.todoList = this.todoList.map((todo, i) => {
return {
...todo,
isChecked: index === i ? !todo.isChecked : todo.isChecked
}
})
},
handleDelete(index) {
this.todoList.splice(index, 1)
}
}
}
</script>
<style scoped lang="scss">
.el-aside {
padding: 20px 10px 20px 20px;
}
.list {
padding: 0;
}
.todo {
height: 60px;
list-style: none;
display: flex;
justify-content: space-between;
.title {
&.checked {
text-decoration: line-through;
}
}
}
.icons i {
padding-right: 10px;
transition: all 0.5s ease-in;
&:hover {
transform: scale(1.5);
color: #2cb696;
}
}
</style>
長くまりましたが、解説をします
styleタグにscopedとlang="scss"と付いています
lang="scss"が付いたstyleタグ内ではscssが記述できるようになります
scopedは、css、scssが記述しているvueファイルのみで使用可能となります、なので他のコンポーネントに予期せぬ影響が及ばなくなるメリットがあります
templateタグは大きく2つの部分になります
el-asideとel-mainで、それぞれelement-uiのコンポーネントです
el-aside側ではinputフォームを、el-mainでは作成したtodoを表示します
el-formもelement-uiのコンポーネントで、formタグのラッパーだと思っていただければと思います(参考 https://element.eleme.io/#/en-US/component/form)
基本的な使い方は、el-formの中にel-form-itemその中にel-inputを入れて使います
el-formの嬉しいところは、バリデーションがすごく簡単にできることです
el-formのバリデーションの使い方
el-formのrulesにどういうバリデーションをしたいかのルールを渡します
scriptタグ内のdataプロパティにrulesとありますが、これが今回のルールです(下画像の赤枠のこと)
required: trueでinputは必須項目にする
messageはエラー発生時に表示する文言
trigger: 'change'で入力値が変わるたびにバリデーションを発火させます
el-formに:rules="rules"と書くことでルールを渡すのは完了です
さらにel-form-inputのpropにinputと渡してください
ここで注意なのが、el-form-inputのprop(input)、rulesのキー(input)と、
el-inputのv-modelに渡すキー(form.inputのinput)はinput(同じ名前)で統一させる必要があります
一度文字を入力し、全て消すと下画像のようにエラーメッセージが表示されます
el-inputタグにv-model.trim="form.input"とありますが、これはscriptタグのdata内のform.inputを指します
これで何か文字を入力すれば、入力した文字列が、form.inputに入ります
v-model.trimのtrimは入力値の前後の空白を自動で排除してくれます
todoの作成
先のel-formで、@submit.native.prevent="handleAdd"とありますが、ここでtodoの作成を行います。
@submit.native.preventとすることで、inputタグに何か入力しenterキーを押すだけでhandleAddが発火します
handleAddはscriptタグ内のmethodsに書きます
handleAdd() {
this.todoList.push({
title: this.form.input,
isChecked: false
})
this.$refs.form.resetFields()
}
this.todoListはscriptタグのdata内のtodoListを指します
todoListはarrayなので、pushメソッドは使って配列にtodoを追加します
titleはtodoのタイトル、isCheckedはtodoが完了したかを表す予定で初期値はfalseで設定します
this.$refs.form.resetFields()
$refsを使うことで、templateタグ内でrefを記述しているタグを取得することができます
今回はel-formにいref="form"で設定しているのでthis.$refs.formでこのel-formを取得できます
el-formに対してresetFields()を発火させることで入力欄を空白に戻すことができます
文字を入力すると右側にxが表示されますが、これを押下すると入力欄がクリアされます
これはel-inputにclearableを記述することでできます
el-mainの部分
ここでは作成したtodoを表示します
v-forでtodoList内のtodoの数の分だけループを回してliタグを生成します
さらに:keyにindexを指定します、このkeyのおかげでVueがどのliタグのことかわかるようになります
liタグに:class="todo.isChecked && 'checked'"とありますが、todoのisCheckedがtrueならcheckedクラスをつけるという意味です
iiタグが2つありますが、これはtodoを完了するアイコンと、削除するアイコンです
それぞれに@clickをつけてアイコンをクリックしたら@click="ここ"が発火するようにします
<i class="el-icon-check" @click="handleCheck(index)"></i>
checkアイコンではhandleCheckをindexを引数に発火させます
// indexはtodoListの何番目のtodoがクリックされたかのindex
handleCheck(index) {
// iはループのインデックス
this.todoList = this.todoList.map((todo, i) => {
return {
...todo,
// iとindexが一致(クリックされたtodo)の場合、isCheckedの値を反転させる
isChecked: index === i ? !todo.isChecked : todo.isChecked
}
})
},
deleteアイコンではhandleDeleteをindexを引数に発火させます
// indexはtodoListの何番目のtodoがクリックされたかのindex
handleDelete(index) {
// クリックされたtodoをtodoListから削除する
this.todoList.splice(index, 1)
}
これで作成されたtodoが表示されるはずです
Vuexを導入する
todoListをpages/index.vueで管理してますが、これをvuexに移行します
大規模なアプリ開発ではほとんどの場合、todoListなどのデータをvuexに状態管理します
各ページからデータをアクセスするときに、vuexで一元管理していれば、管理しやすいからです
storeディレクトリにtodo.jsを作成し、下のように記述してください
export const state = () => ({
todoList: []
})
export const getters = {
todoList({ todoList }) {
return todoList
}
}
export const actions = {
handleAdd({ commit }, title) {
commit('add', { title, isChecked: false })
},
handleCheck({ commit }, index) {
commit('check', index)
},
handleDelete({ commit }, index) {
commit('remove', index)
}
}
export const mutations = {
add(state, todo) {
state.todoList.push(todo)
},
check(state, index) {
state.todoList = state.todoList.map((todo, i) => {
return {
...todo,
isChecked: index === i ? !todo.isChecked : todo.isChecked
}
})
},
remove(state, index) {
state.todoList.splice(index, 1)
}
}
vuexは4つの部分からなります
データを保存するstate
データを取得するためのgetters
api通信をしたり、vueファイルから呼び出すためのハンドラーとなるactions
stateを変化させりmutationsです
stateにtodoListを持たせ、gettersにもtodoListを与えます
actionsにはpages/index.vueで作成したmethodsと同い名前で用意し、commitを発火させます
mutationsではstate.todoListに変化を加えます
これでvuexの設定が完了なので、pages/index.vueとのつなぎこみを行います
pages/index.vueのscriptタグ内を下のようにしてください
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
data() {
return {
form: {
input: ''
},
rules: {
input: [
{ required: true, message: '何か入力してください', trigger: 'change' }
]
}
}
},
computed: {
...mapGetters({
todoList: 'todo/todoList'
})
},
methods: {
...mapActions({
add: 'todo/handleAdd',
check: 'todo/handleCheck',
remove: 'todo/handleRemove'
}),
handleAdd() {
if (this.form.input) {
this.add(this.form.input)
this.$refs.form.resetFields()
}
},
handleCheck(index) {
this.check(index)
},
handleDelete(index) {
this.remove(index)
}
}
}
</script>
import { mapActions, mapGetters } from 'vuex'
でmapActions, mapGettersを使えるようにします
mapActionsはvuexのactionsをvueコンポーネント内のmethodsとして使えるようにし、mapGettersはvuexのgettersをvueコンポーネント内のcomputedとして使えるようにします
dataプロパティからtodoListを消している
vuex内にtodoListを移行したためです
下のようにcomputedに書くことで、this.todoListでtodo.jsのtodoListにアクセスすることができます
computed: {
...mapGetters({
todoList: 'todo/todoList'
})
},
methodsについてはmapActionsを設定します
this.addでtodo.jsのhandleAddにアクセスすることができます
this.checkでtodo.jsのhandleCheckにアクセスすることができます
this.removeでtodo.jsのhandleRemoveにアクセスすることができます
そしてそれぞれをhandle~メソッド内で呼び出します
methods: {
...mapActions({
add: 'todo/handleAdd',
check: 'todo/handleCheck',
remove: 'todo/handleRemove'
}),
handleAdd() {
if (this.form.input) {
this.add(this.form.input)
this.$refs.form.resetFields()
}
},
handleCheck(index) {
this.check(index)
},
handleDelete(index) {
this.remove(index)
}
}
以上でvuexへの移行は完了です
Netlifyへデプロイ
Netlifyとは静的サイトやSPAを簡単にホスティングすることのできるサービスです。Sign upから会員登録と、githubも必要になりますので、githubアカウントをお持ちでない方はこちらも登録よろしくお願い位します
下準備
githubに作成したものをpushします
Repository nameとDescriptionを入力し、Create repositoryを入力してください
ターミナル上で以下のコマンドでコミットしてください
git add .
git commit -m 'initial coommit'
create repositoryを押下後に遷移するページにて赤枠の箇所のコマンドをターミナルで叩いてください
これで下準備完了です
Nelifyの設定
下画像のNew site from Gitを押下してください
下画像の赤枠のGitHubを押下
自分が作成したGitHubのリポジトリを選択する
Build commandに npm run build
Publish directoryに dist
をそれぞれ入力し、Deploy siteを押下
しばらくするとデプロイが完了し、urlができるので、そこをクリックすれば作成したアプリを確認できる
以上で、チュートリアル終わりです!
こういうチュートリアル作成に慣れていないので拙い点がありますが、
わかりにくいところ、もっと解説してほしい点があればコメントください
追記修正いたします!
あと、何かしらリアクションいただけるとうれしいです
フォロー、スキ、サポートお待ちしております
では、お疲れ様です!