Nuxt.jsとは?
Nuxt.jsは、
Vue.js をベースにした JavaScript フレームワークです。**「ナクスト」**と読みます。
間違えて「ヌクスト」と読む人がいますが、間違いです。気をつけましょう。
公式ドキュメント
Nuxt生誕
2016年にAlexandre Chopin(アレクサンドラ・ショパン)、Sébastien Chopin(セバスチャン・ショパン)、Pooya Parsa(プーヤ・パラサ)氏らによりリリースされました。
Nuxt.js(React.jsをベースにしたJavascriptフレームワーク)に触発されて作られたらしいです。
npmとyarnみたいな関係ですね。
2017年以降、Vue.jsとともに、飛躍。Javascript戦国時代の突入です。
Nuxtが使われているサイト例
実際にどういうサイトに使われているのか見てみましょう。
代表的な例を下記にまとめましたが、
もちろんこれ以外にもたくさんのサイトでNuxtが使われています。
####代表的なNuxt実用例
デジタル庁にも使われているとはビックリです。
Nuxtの特徴(メリット)
自分が調べた限りでのNuxtの特徴を羅列的にまとめてみました。
###学習面
###機能面
###実装面
- ルーティングが簡単に設定(ルーティングの自動生成)
- 3つのモードで柔軟なサイト設計ができる
- metaタグの管理をJavascriptのオブジェクトベースで記載できる
- npm(yarn)のパッケージを利用できる
- 独自にパッケージが利用できる
###その他
###学習コストが低い
Nuxtは学習コストが低い、、、みたいです。
これは賛否両論あるかと思うのですが、
個人的にはやはりVue経験者に限るって付け足したいですね。
バイク免許取るのに車の免許持っていた方が有利なのに似てる。
###環境構築が容易
Nuxt.jsは簡単に環境構築することができます。
特にこだわりがなければ、特定のディレクトリに移動して、下記を実行するだけで、使えるようになります。
npx create-nuxt-app <project-name>
※これを実行するのにnpmのインストールが必要です。
###日本語ドキュメントが多い
かなりモダンなNuxtですが、意外にも日本語ドキュメントが多いです。
公式ドキュメントも日本語版があり、ドキュメントの中でも読みやすい印象ですね。
とはいえ、Vueなどに比べたら、日本語ドキュメントは少ないので、
英語が読めるのはやはり有利です。(当たり前だけど)
##SEOへの悪影響を小さく留めることができる
そもそもSEOとは?ってなるかと思うのですが、
要するに、デプロイされたWebサイトが
Googleの検索エンジンに認知されて、上位表示させるために使われる評価基準みたいなものです。
Vueに対して、NuxtはSEOが強いと言われています。
理由としては、NuxtがSSR(サーバーサイドレンダリング)を利用しているからです。
###典型的なSPAの場合
####動作順序
- DOMのロード
- AJAXでバックエンドからデータを読み込む
上記の動作だと、
SEOパーサー(解析作業)でDOM要素が全て読み込まれないので、SEOに悪影響があるようです。
###SSR(サーバーサイドレンダリング)の場合
####動作順序
- Vueのコンポーネントをサーバ上のHTML文字列にレンダリング
- 直接ブラウザに送信
- マークアップをクライアント上に提供
Nuxt(SSR)は上記の工程を踏みます。
そうすると、SEOパーサー(解析作業)で素早くDOM要素が読み込ます。
なので、SEOには対して悪影響が少ないみたいです。
とはいえ、SEOというのは、抽象的な概念なので、
何が正解で何が不正解というのは正確には測れません。
なので、キッパリと
「Vueは、SEOが弱い」、
「Nuxtは強い」
とも言い切れないと思います。
参考記事
Vue.jsよりもNuxt.js:使うべき理由とタイミング
###通信速度が速い(Code splitting)
通信速度が速い理由として、Code splittingという仕組みがあります。
Code splittingの仕組みのおかげで、
必要としない余分なページをブラウザに渡す必要がないので、パフォーマンスが向上します。
Code splittingに関しては本記事では深掘りしませんので、興味のある方は調べてみてください。
###SSR(サーバーサイドレンダリング)
ウェブページのレンダリングをブラウザの代わりにサーバー上で行う、画面の表示において有用なアプリケーションの機能です。
####SSRではない場合
ブラウザ上でレンダリングを行うため、ページの読み込みに時間がかかる
####SSRの場合
レンダリングをサーバ上で行うので、ページの読み込みに時間がかからない
###PWAモジュールが用意されているので、PWA対応できる
PWAとは、
モバイル端末でウェブサイトを表示するときに、
ネイティブアプリのような動作を可能にする仕組みのことです。
Webサイトがアプリのように動くってイメージでいいと思います。
####機能例
- プッシュ通知
- アイコンのホーム画面の表示
- オフラインでの使用
###ルーティングが簡単に設定(ルーティングの自動生成)
自動的に生成されるルート
多くのウェブサイトでは単一のページではなく、複数ページ (例えばホームページ、概要ページ、お問い合わせページなど) を持っています。これらのページを表示するにはルーターが必要です。そこで vue-router の出番です。Vue アプリケーションで動作させる際に、設定ファイル(例えば、router.js)を設定し、全てのルートを手動で追加しなければなりません。Nuxt では pages ディレクトリにある Vue ファイルに基づいて、vue-router の設定を自動的に生成します。つまり、ルーターの設定を書く必要はもうありません! Nuxt はまた全てのルートに対して自動でコード分割を行います。
つまり、アプリケーションでルーティングを行うのに必要なことは pages フォルダに .vue ファイルを作成するだけです。
上記は、公式ドキュメント:ルーティングの引用です。
####ルーティング方法(Vueとの比較)
####Vueの作業
- Vueファイルを作成する
- 設定ファイル(router.js)などにパスの設定を記述
####Nuxtの作業
1.Vueファイルを作成する
あら不思議。Vueファイルを作るだけでルーティングが行えます。
###3つのモードで柔軟なサイト設計ができる
Nuxtには3つのモードが用意されています。
- Universal App(SSR)
- Single Page App(SPA)
- Static App(プリレンダリング)
####Universal App (SSR)
Nuxtの醍醐味ともいえる、サーバーサイドレンダリング(SSR)モードです。
メリットとしてSEOに強いとかページの読み込みが速いとかがあります。
####Single Page App(SPA)
通常のVueスタイルです。
シングルページアプリケーションとして、機能します。
レンダリングは、クライアント側で行われるので、
読み込みに少し時間がかかる傾向にあります。
####Static App(プリレンダリング)
SSRとの違いは、htmlファイルの生成タイミングです。
SSRはクライアントからリクエストがあったときに作成しますが、Static App(プリレンダリング)はビルドのときに
ページごとのhtmlファイルを生成します。
ページによってモードが切り替えられるので便利です。
###metaタグの管理をJavascriptのオブジェクトベースで記載できる
nuxt.config.jsにhead内の設定(metaタグとか)を記述できます。
下記は公式ドキュメント:メタタグとSEOの引用です。
export default {
head: {
title: 'my website title',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: 'my website description'
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
}
}
オブジェクトにまとめるとわかりやすいですなぁ。
###npm(yarn)のパッケージを利用できる
当然といえば、当然ですが、npm(yarn)のパッケージが使えます。
これはVueも同様です。
パッケージは、Nuxtチームとコミュニティの方々がたくさん作成してくれています。
感謝感激です。
nuxtで使えるパッケージ一覧
###独自のパッケージを利用できる
独自のパッケージを作ることもできます。
npm モジュールとしてパッケージ化してもよし。
プロジェクトのソースコードに直接含めるもよし。
詳しくは
公式ドキュメント:モジュールディレクトリ
###コミュニティによる改善
Vue.jsの普及を目的としたコミュニティも充実しています。
このコミュニティにより、
VueやNuxtの公式ドキュメントの翻訳や、プラグインの開発などが行われており、
VueやNuxtがより便利に使われるようになっております。
技術が日々進化しているのもこの方々のおかげとも言えます。
公式ホームページ
Vue.js 日本ユーザーグループホームページ
Nuxtのセットアップ
Nuxtのセットアップ手順をご紹介いたします。
###動作環境
- PC: MacBook Pro (13-inch, 2019, Two Thunderbolt 3 ports) インテルチップ
- MacOS: BigSur v11.6
- node: v14.18.1
- npm: v6.14.15
- nuxt: v2.15.8
セットアップ手順
1. 特定のディレクトリに移動する
まず、自分が作業するディレクトリに移動します。
どこでも大丈夫です。
自分は、、、createというディレクトリの中にアプリを作っていきます。
sakatsumemasato@sakatsumemasatonoMacBook-Pro create %
2. npx create-nuxt-app コマンドで環境を構築する
2-1 npx create-nuxt-appを実行する
% npx create-nuxt-app nuxt-practice
このコマンドを実行することで、
nuxtのアプリに必要な環境を揃えてくれます。
手動で環境を作ることもできますが、特にこだわりがなければ、このコマンドを使用しましょう。
注意点
- 「nuxt-practice」はディレクトリの名前です。これはどんな名前でも大丈夫です。
- 「npx create-nuxt-app」はnpm v6.1 以上がインストールされていることが前提条件です。
####2-2 プロジェクト名を入力する
npx create-nuxt-appをしたらいくつか質問が来るので、自分の構築したい環境に合わせて回答しましょう。
まず、最初の質問ですが、プロジェクトの名前が聞かれます。
? Project name: (nuxt-practice)
特にこだわりがなければ、ディレクトリの名前と同じでいいです。
? Project name: nuxt-practice
入力してEnterを押します。
####2-3 Javascriptの種類を選択する
Javascriptの種類が聞かれるので、
作りたい環境に合わせて選択しましょう。
? Programming language:
❯ JavaScript
TypeScript
選択項目
- Javascript
- TypeScript
今回は、Nuxtの学習に集中したいので、**「Javascript」**を選択肢します。
####2-4 パッケージマネージャーの種類を選択する
パッケージマネージャーの種類が聞かれるので、
好みで選択しましょう。
? Package manager: (Use arrow keys)
❯ Yarn
Npm
選択項目
- Yarn
- Npm
今回は**「npm」**を使用します。
####2-5 UI(デザイン)フレームワークの種類を選択する
UI(デザイン)で使うフレームワークについて聞かれます。
必要な場合は、その項目を選択しましょう。
不要な場合は「None」を選択しましょう。
? UI framework: (Use arrow keys)
❯ None
Ant Design Vue
BalmUI
Bootstrap Vue
Buefy
Chakra UI
Element
Framevuerk
Oruga
Tachyons
Tailwind CSS
Windi CSS
Vant
View UI
Vuetify.js
選択項目
- None
- Ant Design Vue
- BalmUI
- Bootstrap Vue
- Buefy
- Chakra UI
- Element
- Framevuerk
- Oruga
- Tachyons
- Tailwind CSS
- Windi CSS
- Vant
- View UI
- Vuetify.js
上記のフレームワークの公式サイトのリンクを貼りましたので、興味のある方はクリックしてみてください。
今回は、**「None」**を選択します。
####2-6 任意のモジュールを選択する
任意のモジュールを入れるか聞かれます。
選択すると対応するパッケージがインストールされます。
こちらは複数選択可能です。
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ Axios - Promise based HTTP client
◯ Progressive Web App (PWA)
◯ Content - Git-based headless CMS
※操作
スペースキー:選択
aキー:全て選択
iキー:選択を反転する
選択項目
- Axios - Promise based HTTP client
- Progressive Web App (PWA)
- Content - Git-based headless CMS
Axios
「nuxtjs/axios」というパッケージがインストールされます。axiosは、Ajax通信を簡単に行うことができるJavascriptライブラリのことです。
Progressive Web App (PWA)
「nuxt pwa」というパッケージがインストールされます。PWAに対応したサイトが作れるようになるようです。
Content - Git-based headless CMS
「nuxt/content」というパッケージがインストールされます。Nuxtでマークダウン形式のファイルをCMS風に使用するためのパッケージらしいです。
今回は**「Axios」**だけを選択します。
####2-7 Lint(コードチェック・コード整形)ツールを選択する
Lintツールについて聞かれます。
こちらも複数選択可能です。
Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ ESLint
◯ Prettier
◯ Lint staged files
◯ StyleLint
◯ Commitlint
選択項目
- ESLint
- Prettier
- Lint staged files
- StyleLint
- Commitlint
ESLint
コードを静的に分析/修正/カスタマイズするツール
Prettier
ソースコードを整形してくれるツール
Lint staged files
git commitする際に、eslintによるチェックを行うツールで、
huskyというパッケージとセットで使われることが多いです。
StyleLint
CSSのためのLintツール。
Commitilint
gitにおけるコミットメッセージを特定のフォーマットのみに制限するできるツール。
今回は、**「ESlint」**だけ選択します。
2-8 テスティングフレームワークを選択する
テスティングのフレームワークを聞かれます。
? Testing framework: (Use arrow keys)
❯ None
Jest
AVA
WebdriverIO
Nightwatch
選択項目
- None
- Jest
- AVA
- WebdriverIO
- Nightwatch
Jest
シンプルさを重視した、快適な JavaScript テスティングフレームワーク
AVA
サイズが軽量で並列処理で高速で動作するJavascriptテスティングフレームワーク
WebdriverIO
Node.js用の次世代ブラウザとモバイル自動化テストフレームワーク
Nightwatch
Node.jsで記述された、
WebアプリケーションおよびWebサイト向けの統合された使いやすいエンドツーエンドのテストソリューション
今回はテストしないので、**「None」**を選択します。
####2-9 ページの種類を選択する
作成するページの種類について聞かれます。
Rendering mode: (Use arrow keys)
❯ Universal (SSR / SSG)
Single Page App
選択項目
- Universal (SSR / SSG)
- Single Page App
Universal (SSR / SSG)
SSR(サーバーサイドでレンダリング)/SSG(スタティックサイトジェネレーション)を使用する
Single Page App
SPA(シングルページアプリケーション)を使用する(Vueと同じ)
今回は、Nuxtの機能をチェックしたいので、
**「Universal (SSR / SSG)」**を選択します。
####2-10 デプロイ先を選択する
? Deployment target: (Use arrow keys)
❯ Server (Node.js hosting)
Static (Static/Jamstack hosting)
選択項目
- Server (Node.js hosting)
- Static (Static/Jamstack hosting)
Server (Node.js hosting)
サーバーホスティングとは、Node.jsサーバーでNuxtを実行することを意味します。ユーザーがページを開くと、ブラウザはサーバーにそのページを要求します。Nuxtはリクエストを処理し、ページをレンダリングして、結果のページとそのすべてのコンテンツを送り返します。
Static (Static/Jamstack hosting)
Nuxtは静的サイトジェネレーターとしても機能します。Nuxtアプリケーションを静的にレンダリングし、サーバーなしでユニバーサルアプリのすべてのメリットを享受できます。このnuxt generateコマンドは、Webサイトの静的バージョンを生成します。ルートごとにHTMLを生成し、dist/ディレクトリ内の独自のファイル内に配置します。これにより、パフォーマンスが向上し、SEOとオフラインサポートが向上します。
引用:Nuxt-展開ターゲット
今回は、ちょっとよくわからなかったので、**「Server (Node.js hosting)」**を選択しておきます。
####2-11 開発用ツールを選択する
開発用ツールについて聞かれます。
こちらも複数選択できます。
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ jsconfig.json (Recommended for VS Code if you're not using typescript)
◯ Semantic Pull Requests
◯ Dependabot (For auto-updating dependencies, GitHub only)
選択項目
- jsconfig.json (Recommended for VS Code if you're not using typescript)
- Semantic Pull Requests
- Dependabot (For auto-updating dependencies, GitHub only)
jsconfig.json (Recommended for VS Code if you're not using typescript)
VSCode(Visual Studio Code)で開発する際は入れたほうが良いらしい。
Semantic Pull Requests
commitのメッセージとPull Requestにおけるタイトルのつけ方をチェックしてくれる。
Dependabot (For auto-updating dependencies, GitHub only)
package.jsonやgo.modといったマニュフェストファイルをみて、古いライブラリやセキュアでないもの(安全でないもの)を調べてくれる。
今回は、**「jsconfig.json (Recommended for VS Code if you're not using typescript)」**だけを
選択します。
####2-12 継続的インテグレーションを選択する
継続的インテグレーションについて聞かれます。
継続的インテグレーションとは、
開発者が自分のコード変更を定期的にセントラルリポジトリにマージし、その後に自動化されたビルドとテストを実行する DevOps ソフトウェア開発の手法
つまり、何かアクションが起きた時(プッシュやマージした時)に、自動的にビルドやテストを実行してくれる機能です。
? Continuous integration: (Use arrow keys)
❯ None
GitHub Actions (GitHub only)
Travis CI
CircleCI
選択項目
- None
- GitHub Actions (GitHub only)
- Travis CI
- CircleCI
GitHub Actions (GitHub only)
githubでの動作をトリガーとして、テストやビルドなどを自動的に行ってくれる
Travis CI
GitHubと連携した自動テスト実行サービス、最小限の設定のみでCIを導入できるのが特徴
CircleCI
Saas型のCI/CDサービス
今回はCIは関係ないので、**「None」**を選択します。
####2-13 バージョン管理システムを選択する
? Version control system: (Use arrow keys)
❯ Git
None
選択項目
- Git
- None
Gitを使ってコード管理するので**「Git」**を選択します。
環境構築完了
下記が出たら成功です!!
🎉 Successfully created project nuxt-practice
lsコマンドで確認してみると、
% ls
nuxt-practice
ディレクトリができています。
ちなみに今回のオプションのまとめ
create-nuxt-app v3.7.1
✨ Generating Nuxt.js project in nuxt-practice
? Project name: nuxt-practice
? Programming language: JavaScript
? Package manager: Npm
? UI framework: None
? Nuxt.js modules: Axios - Promise based HTTP client
? Linting tools: ESLint
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Server (Node.js hosting)
? Development tools: jsconfig.json (Recommended for VS Code if you're not using typescript)
? Continuous integration: None
? Version control system: Git
###3 プロジェクトのディレクトリに移動する
先ほど、npx-nuxt-appで作ったディレクトリに移動します。
cd nuxt-practice
###4 npm run dev でアプリを起動させる
下記のコマンドで、アプリを起動させます。
npm run dev
> nuxt
╭───────────────────────────────────────╮
│ │
│ Nuxt @ v2.15.8 │
│ │
│ ▸ Environment: development │
│ ▸ Rendering: server-side │
│ ▸ Target: server │
│ │
│ Listening: http://localhost:3000/ │
│ │
╰───────────────────────────────────────╯
ℹ Preparing project for development 20:04:11
ℹ Initial build may take a while 20:04:11
ℹ Discovered Components: .nuxt/components/readme.md 20:04:11
✔ Builder initialized 20:04:11
✔ Nuxt files generated 20:04:11
✔ Client
Compiled successfully in 5.14s
✔ Server
Compiled successfully in 4.63s
ℹ Waiting for file changes 20:04:18
ℹ Memory usage: 254 MB (RSS: 360 MB) 20:04:18
ℹ Listening on: http://localhost:3000/ 20:04:18
これが出たら、成功🎉
###Nuxtのディレクトリ構造
root/
│─ .nuxt
│─ components
│─ node_modules
│─ pages
│─ static
│─ store
│─ .editorconfig
│─ .eslintrc.js
│─ .gitignore
│─ jsconfig.json
│─ nuxt.config
│─ package-lock.json
│─ package.json
│─ README.md
今回はNuxt特有のものだけ簡単に紹介します。
.nuxt
ビルドディレクトリ。
devやbuildコマンドを再度実行すると、ここに保存されたものはすべて再生成される。
components
Vue.jsのコンポーネントを管理する。
pages
アプリケーションのビューファイル・ルーティングファイルを入れる。
static
変更される可能性が低い静的ファイルを置く。
store
Vuexストアのファイルを入れる。
nuxt.config.js
Nuxt.jsのカスタム設定を記述するファイル。
詳しくは
ディレクトリ構造 - Nuxt.js
##NuxtでTodoアプリを作ってみた。
環境構築が終わったところで、簡単にTodoアプリを作っていきたいと思います。
さて、やっていきましょう。
###作業手順
###1.使わないファイルを削除する
npx create-nuxt-appをすると、デフォルトで不要なファイルまで作成します。
いらないファイルは削除していきましょう。
あるとなんか鬱陶しいです。
今回使わないファイル
- conponents/NuxtLogo.vue
- conponents/Tutorial.vue
- conponents/favicon.icon
###2.ポート番号を変更する
デフォルトでポート番号が3000になっているのですが、これだと何やら、動きが遅かったので、
ポート番号を8000に変えました。
環境によって動きが遅い場合があるので、遅かったらポート番号を変えましょう。
ポートの変え方は、nuxt.config.jsにポート番号の設定を追加します。
server: {
port: 8000 // default: 3000
},
それで再度、npm run devしなおします。
そうすると、localhost:8000で動くようになります。
こうすることで、スムーズにNuxtが動くようになりました。
※localhost*3000で動くようならそのままでOK
###3.header部分の作成
まず、header部分を作成していきます。
####3-1 pagesを下記のように書きます。
templateのタグの中に、headerコンポーネントを入れています。
<template>
<header />
</template>
####3-2 headerコンポーネントの作成
まだheaderコンポーネントが作成されていないので、localhost:8000(3000) にログインしても何も表示されません。(エラーになります。)
なので、headerコンポーネントを作っていきます。
components/Header.vueを作成し、下記のように書きます。
<template>
<header>
<div class="logo">
<img src="logo.png" alt="logo" />
</div>
</header>
</template>
<style>
header {
text-align: center;
}
</style>
今回、画像は、staticの中に入れました。
これでlocalhost:8000(3000)にアクセスします。
headerができました。
###4.Todo部分の作成
Todo部分の作成部分ですが、
手順通りにひとつひとつ解説していくのは、
骨の折れる作業で、さらに実装し終えてから記事を書いているので、まとめるのも大変です。
なので、今回は処理の流れのみ解説させてください(すいません。)
記事作成後かなり、リファクタリングしているので、もし、しっかり作りたい方は、
坂爪誠人のgithubを参考に作成してみてください。
リファクタリング前なので、ちょっとコードが汚いですが、参考程度にご覧ください。
それではまず、出来上がったコードを紹介します。
<template>
<div>
<Header />
<Todo />
</div>
</template>
components/Todo.vueのマークアップ部分
<template>
<section class="container">
<div class="wrap">
<div class="taskAdd">
<input
id="input"
v-model="content"
type="text"
name="input"
placeholder="タスクを入力してください"
/>
<button @click="insert">
追加
</button>
</div>
<div class="filter">
<div v-for="(btn, index) in buttons" :key="index" class="buttonWrap">
<button :class="{'filterActiveBgColor' : btn.colorFlag}" @click="getfilter( index )">{{ btn.value }}</button>
</div>
</div>
<div v-for="(todo,index) in getTodos" :key="index" class="task">
<div class="taskWrap">
<p class="taskContent" :class="{'completeContent' : todo.state }">
{{ todo.content }}
</p>
<button :class="{'completeButton' : todo.state }" @click="changeState({ todo, index })">
完了
</button>
</div>
</div>
</div>
</section>
</template>
components/Todo.vueのスクリプト部分
<script>
export default {
data() {
return {
content: '',
button: '',
buttons: [
{ value: '全て', colorFlag: true },
{ value: '完了', colorFlag: false },
{ value: '未完了', colorFlag: false }
]
}
},
computed: {
getTodos() {
const button = this.button
const allTodos = this.$store.getters.getTodos
const completeTodos = allTodos.filter(todo => (todo.state === true))
const notCompleteTodos = allTodos.filter(todo => (todo.state === false))
if (button === '完了') {
return completeTodos
} else if (button === '未完了') {
return notCompleteTodos
} else {
return allTodos
}
}
},
methods: {
insert() {
if (this.content !== '') {
this.$store.commit('insert', { content: this.content })
this.content = ''
} else {
alert('タスクが入力されていません。')
}
},
changeState(todo) {
this.$store.commit('changeState', todo)
},
getfilter(index) {
this.button = this.buttons[index].value
const changeButtonsColorFlag = this.buttons.map((button, i) => {
return {
...button,
colorFlag: index === i
}
})
this.buttons = changeButtonsColorFlag
}
}
}
</script>
components/Todo.vue スタイル部分
<style>
.container {
height: 100vh;
background-color: #f3f3f3;
padding:100px 0;
}
.wrap {
width:50%;
margin:0 auto;
}
/* input */
.taskAdd {
display:flex;
justify-content: space-between;
height:35px;
margin-bottom:50px;
}
.taskAdd input {
width:80%;
}
.taskAdd button {
width:10%;
}
/* filter */
.filter {
margin-bottom:10px;
display: flex;
}
.filter button {
border-radius: 10px 10px 0 0;
width:100px;
height:30px;
}
/* task */
.task {
border-bottom:1px solid #000;
padding:30px 0;
}
.taskWrap {
display:flex;
justify-content: space-between;
align-items: center;
width:80%;
}
.task .taskContent {
width:82%;
}
.task button {
width:15%;
height: 35px;
border-radius: 20px;
}
/* task toggle style */
.completeContent {
text-decoration: line-through;
}
.completeButton {
background-color: #707070;
}
/* filter avtive style */
.filterActiveBgColor {
background: #fff;
}
</style>
Vuexの部分(state管理)
import Vuex from 'vuex'
const createStore = () => {
return new Vuex.Store({
state: {
todos: []
},
getters: {
getTodos: state => state.todos
},
mutations: {
insert(state, obj) {
state.todos.unshift({
content: obj.content,
state: false,
status: '未完了'
})
},
changeState(state, obj) {
obj.todo.state = !state.todos[obj.index].state
}
}
})
}
export default createStore
続いて、大まかな作成手順と解説(処理の流れ)を行います。
####4-1 ファイルの作成
まず、必要ファイルの作成を行います。
- components/Todo.vue ⇨ Todo(タスク管理)機能を行うコンポーネント
- store/index.js ⇨ state(状態管理)を行うファイル(vuex)
今回は、state管理にvuexを使います。
詳しくは:vuexとは何か?|Vuex
####4-2 pages/index.vueにTodoコンポーネントを追加
Headerの下にTodoコンポーネントを付け加えます。
その際、コンポーネントが二つ以上になると、エラーになるので、
Headerと、Todoをdivタグで囲みましょう。
これでTodoコンポーネントを作成したら、ページに表示されるようになります。
まだ作成してないので、エラーが出ていると思いますが、気にせず次に進みます。
<template>
<div>
<Header />
<Todo />
</div>
</template>
####4-3 Todo タスク追加機能(解説)
すいません、こちらから解説のみとさせていただきます。
Todoのタスクを追加する機能です。
一般的な、Inputタグに入力して、ボタンを押すとタスクが入力されるやつです。
処理の流れ
① 追加ボタンを押す
② methodのinsertが実行される
③ 文字がInputタグに入力されていたら、store/index.js(vuex)にcontentを渡す
④ store/index.jsのinsertが実行される
stateの⑤todos(配列)にcontentを含めたobjectを格納
オブジェクトの形式
{ content: obj.content, state: false, status: '未完了' }
⑥ gettersのgetTodosが更新される
⑦ computedのgetTodos()が更新される(今回は、allTodosがgetTodosにかえる)
⑧ getTodosの中のオブジェクトの一覧をv-forで回して表示させる
-省略-
<div class="taskAdd">
<input
id="input"
v-model="content"
type="text"
name="input"
placeholder="タスクを入力してください"
/>
------①------
<button @click="insert">
追加
</button>
</div>
-----省略-----
------⑧------
<div v-for="(todo,index) in getTodos" :key="index" class="task">
<div class="taskWrap">
<p class="taskContent" :class="{'completeContent' : todo.state }">
{{ todo.content }}
</p>
<button :class="{'completeButton' : todo.state }" @click="changeState({ todo, index })">
完了
</button>
</div>
</div>
-----省略-----
<script>
export default {
data() {
return {
content: '',
}
},
computed: {
------⑦------
getTodos() {
const button = this.button
const allTodos = this.$store.getters.getTodos
const completeTodos = allTodos.filter(todo => (todo.state === true))
const notCompleteTodos = allTodos.filter(todo => (todo.state === false))
if (button === '完了') {
return completeTodos
} else if (button === '未完了') {
return notCompleteTodos
} else {
return allTodos
}
}
},
methods: {
------②------
insert() {
if (this.content !== '') {
------③------
this.$store.commit('insert', { content: this.content })
this.content = ''
} else {
alert('タスクが入力されていません。')
}
},
}
}
-----省略-----
</script>
import Vuex from 'vuex'
const createStore = () => {
return new Vuex.Store({
------⑤------
state: {
todos: []
},
------⑥------
getters: {
getTodos: state => state.todos
},
mutations: {
------④------
insert(state, obj) {
state.todos.unshift({
content: obj.content,
state: false,
status: '未完了'
})
}
}
})
}
export default createStore
####4-4 Todo status更新機能
追加したTodoタスクとセットで完了ボタンを設置して、
ボタンを押したら、タスクが完了するというstatus更新機能の解説です。
今回は、完了(完了済)になったボタンの背景色を変更、タスクのcontentに取り消し線を引くスタイルを当てます。
処理の流れ
① 完了ボタンをクリックする
② methodのchangeStateが発火して、引数のtodo(配列)をstore/index.js(vuex)に渡す
③ store/index.jsのmutationsのchangeStateが発火して、stateが更新される
下記の処理で、stateを反対にする(toggle的な処理)
obj.todo.state = !state.todos[obj.index].state
つまり、state.todos[obj.index].stateがtrueの時はfalse、falseの時は、trueにする(④)
④ stateのtodos(配列)が変更される(state.todos[].stateの部分)
{ content: obj.content, state: true, status: '未完了' }
⑤ gettersのgetTodosが更新される
⑥ stateが更新されたので、Todo.vue/getTodosが実行される
⑦ computedのgetTodos()が更新されてcontent画面に表示される
⑧ todo.stateがtrueになっているものに関してはスタイルを当てている
⑨ ⑧のスタイル
-----省略-----
<div v-for="(todo,index) in getTodos" :key="index" class="task">
<div class="taskWrap">
------⑦------
<p class="taskContent" :class="{'completeContent' : todo.state }">
{{ todo.content }}
</p>
------①------
<button :class="{'completeButton' : todo.state }" @click="changeState({ todo, index })">
完了
</button>
</div>
-----省略-----
<script>
computed: {
------⑥------
getTodos() {
const button = this.button
const allTodos = this.$store.getters.getTodos
const completeTodos = allTodos.filter(todo => (todo.state === true))
const notCompleteTodos = allTodos.filter(todo => (todo.state === false))
if (button === '完了') {
return completeTodos
} else if (button === '未完了') {
return notCompleteTodos
} else {
return allTodos
}
}
},
methods: {
-----省略-----
------②------
changeState(todo) {
this.$store.commit('changeState', todo)
},
}
-----省略-----
<script/>
import Vuex from 'vuex'
const createStore = () => {
return new Vuex.Store({
------④------
state: {
todos: []
},
------⑤------
getters: {
getTodos: state => state.todos
},
mutations: {
------③------
changeState(state, obj) {
obj.todo.state = !state.todos[obj.index].state
}
}
})
}
export default createStore
<style>
-----省略-----
/* task toggle style */
------⑨------
.completeContent {
text-decoration: line-through;
}
.completeButton {
background-color: #707070;
}
-----省略-----
</style>
####4-5 絞り込み機能
全て/完了/未完了のボタンでタスクを絞り込む機能です。
処理の流れ
① 全てor完了or未完了のボタンを押す
dataの②buttonsをv-forでループさせて表示させている
③ methodのgetfilterが実行される
- index番号に紐づいたbuttonsのvalueをdataのbuttonに格納する
- map関数でクリックしたbuttonsのcolorFlagをtrueにしてスタイルを変更する
④ ③のスタイル
⑤ computed/getTodosで格納されたbuttonのstateによってgetTodosの値を絞り込み
完了の場合:todo.state === trueのやつだけ値を返す
未完了の場合:todo.state === falseのやつだけ値を返す
それ以外:絞り込みしないでそのままの値を返す
⑥ 絞り込まれたgetTodosを画面上に表示
<template>
<section class="container">
<div class="wrap">
-----省略-----
------①------
<div class="filter">
<div v-for="(btn, index) in buttons" :key="index" class="buttonWrap">
<button :class="{'filterActiveBgColor' : btn.colorFlag}" @click="getfilter( index )">
{{ btn.value }}
</button>
</div>
</div>
-----省略-----
------⑥------
<div v-for="(todo,index) in getTodos" :key="index" class="task">
<div class="taskWrap">
<p class="taskContent" :class="{'completeContent' : todo.state }">
{{ todo.content }}
</p>
<button :class="{'completeButton' : todo.state }" @click="changeState({ todo, index })">
完了
</button>
</div>
</div>
-----省略-----
</div>
</section>
</template>
<script>
export default {
data() {
return {
content: '',
button: '',
------②------
buttons: [
{ value: '全て', colorFlag: true },
{ value: '完了', colorFlag: false },
{ value: '未完了', colorFlag: false }
]
}
},
computed: {
------④------
getTodos() {
const button = this.button
const allTodos = this.$store.getters.getTodos
const completeTodos = allTodos.filter(todo => (todo.state === true))
const notCompleteTodos = allTodos.filter(todo => (todo.state === false))
if (button === '完了') {
return completeTodos
} else if (button === '未完了') {
return notCompleteTodos
} else {
return allTodos
}
}
},
methods: {
insert() {
if (this.content !== '') {
this.$store.commit('insert', { content: this.content })
this.content = ''
} else {
alert('タスクが入力されていません。')
}
},
changeState(todo) {
this.$store.commit('changeState', todo)
},
------③------
getfilter(index) {
this.button = this.buttons[index].value
const changeButtonsColorFlag = this.buttons.map((button, i) => {
return {
...button,
colorFlag: index === i
}
})
this.buttons = changeButtonsColorFlag
}
}
}
</script>
<style>
-----省略-----
------④------
/* filter avtive style */
.filterActiveBgColor {
background: #fff;
}
</style>
##作成物(Todoアプリ)のgithub
Qiitaの記事のコードと、githubのコードがいくつか違うのは、リファクタリングしたためです。
参考にするなら、記事のよりGithubを見た方がいいコードです。(笑)
リファクタリングした後に記事書けばよかった。
直すのめんどくさいのでご了承ください。(すいません。)
Todoアプリ
https://github.com/masato5579/nuxt-practice
##Nuxt.jsを使ってみての所感
Vueもろくに使えないまま、Nuxtの記事を書いているので、
不要な部分や、正しくない情報を書いているかもしれないので、
もし間違い等ありましたら、ご指摘よろしくお願いいたします。
感想としては、Vueがそもそもわからないので、便利なのかもわからないというのが正直な感想です。
Vueより表示速度が早いとかあると思いますが、正直わかるレベルまで到達していないので、
その点に関しては、ベテラン強強エンジニアさんの記事を読んでください。
※Todo作成のくだり、やってることVueと変わらないじゃんいうツッコミはなしでお願いします。
長々と読んでいただきありがとうございました。