やること
- NuxtJSインストール
- ページの追加
- storeの確認
- propsとslotの確認
- $emitの確認
- middlewareの確認
- layoutの確認
コード
プロジェクト作成
~$ npx nuxt-create-app nuxt-sample
設定内容は以下の通り。
? Programming language: JavaScript
? Package manager: Npm
? UI framework: None
? Nuxt.js modules: Axios - Promise based HTTP client
? Linting tools: ESLint, Prettier
? Testing framework: Jest
? Rendering mode: Single Page App
? Deployment target: Server (Node.js hosting)
? Development tools: jsconfig.json, Dependabot
? Continuous integration: None
? Version control system: Git
動かしてみる(当然のように動く)。
~$ cd nuxt-sample
~$ npm run dev
ホットリロードは便利。
構成確認
初期状態は以下の通り。
ファイルには先頭に📄を付加してます。
各ディレクトリとファイルの説明はディレクトリ構成 - NuxtJSと.nuxt - NuxtJSに書いてある。
rootDir
├ .github
│ └ 📄dependabot.yml
├ assets
├ components
├ layouts
├ middleware
├ node_modules
├ pages
├ plugins
├ static
├ store
├ test
├ 📄.babelrc
├ 📄.editorconfig
├ 📄.eslintrc.js
├ 📄.gitignore
├ 📄.prettierrc
├ 📄jest.config.js
├ 📄jsconfig.json
├ 📄nuxt.config.js
├ 📄package-lock.json
├ 📄package.json
└ 📄README.md
middlewareに関してはexpressのmiddleware的な感じ?
pluginsは普通にロジックを切り出して置いておくディレクトリですかね。
ページを追加する
Nuxtはvue-routerをいい感じにラップしてくれているのでページの追加は/pagesにany.vueを追加するだけです。hoge.vue
を追加すれば/hoge
でアクセスできます。/hoge/index.vue
のようにディレクトリを切ってindex.vueを用意する形でもOK。
.vueからstyleやscriptを切り離すならディレクトリを作成した方が良さそうな印象(scriptも共通処理はpluginsに入ると思いますが)。
Vuterのインストール
VSCodeでコードを書くのでVuterをインストール。
最新バージョンをインストールしたらformatOnSave設定と何か干渉しているのかいつまでも保存が終わらない...Getting code actions from ''Vetur', 'Eslint'' takes too long!を参照してversionを0.25.0に下げることで解決。
先人の知恵はありがたい。
store
store確認用のインクリメントボタン、デクリメントボタン、テキストのatomを用意してそれっぽいcomponentを作ってみる。storeも作る。
index.vue
<template>
<div class="container">
<section>
<Counter />
</section>
</div>
</template>
<script>
export default {
// logic
}
</script>
<style scoped>
.container {
margin: 0 auto;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
</style>
/components/atoms/CounterLabel.vue
<template>
<label>{{ $store.state.counter }}</label>
</template>
/components/atoms/IncrementButton.vue
<template>
<button @click="increment">+</button>
</template>
<script>
export default {
methods: {
increment() {
this.$store.commit('increment')
},
},
}
</script>
/components/atoms/DecrementButton.vue
<template>
<button @click="decrement">-</button>
</template>
<script>
export default {
methods: {
decrement() {
this.$store.commit('decrement')
},
},
}
</script>
/components/Counter.vue
<template>
<div>
<div>
<CounterLabel />
</div>
<div>
<IncrementButton />
<DecrementButton />
</div>
</div>
</template>
/store/index.js
export const state = () => ({
counter: 0,
})
export const mutations = {
increment(state) {
state.counter++
},
decrement(state) {
state.counter--
},
}
できた(気がする
props / slot
propsとslotの使い分けとかは割愛。
/components/atoms/IncrementButton.vue
<template>
<button @click="increment">{{ text }}</button>
</template>
<script>
export default {
props: {
text: {
type: String,
default: 'Increment',
},
},
methods: {
increment() {
this.$store.commit('increment')
},
},
}
</script>
/components/atoms/DecrementButton.vue
<template>
<button @click="decrement"><slot /></button>
</template>
<script>
export default {
methods: {
decrement() {
this.$store.commit('decrement')
},
},
}
</script>
/components/Counter.vue
<template>
<div>
<div>
<CounterLabel />
</div>
<div>
<IncrementButton text="+" />
<DecrementButton><slot>Decrement</slot></DecrementButton>
</div>
</div>
</template>
slotを複数使う時は名前付きslotを使いましょうねと。
$emit
Child Componentの値やイベントを親に渡す時に使う。値はstoreで管理すれば良いし、イベントはmethodを渡せば良いのでは???って思ってるけど多分何か違うんだろうなぁ...。
一気に見通しが悪くなった気がするけど慣れてないだけかな...。
/components/Counter.vue
<template>
<div>
<div>
<CounterLabel />
</div>
<div>
<IncrementButton text="+" @welcome="say" />
<DecrementButton><slot>Decrement</slot></DecrementButton>
<SayButton @sayCount="say" />
</div>
</div>
</template>
<script>
export default {
methods: {
say(val) {
alert(val)
},
},
}
</script>
/components/atoms/SayButton.vue
<template>
<button @click="sayCount">Say</button>
</template>
<script>
export default {
methods: {
sayCount() {
this.$emit('sayCount', this.$store.state.counter)
},
},
}
</script>
middleware
レンダリング前に処理をしたい場合はmiddlewareを使う。middlewareディレクトリ内に処理を定義して、pagesディレクトリ内の.vueでどの処理を適用させるか定義する。
/middleware/sampleMiddleware.js
export default function (context) {
console.log('middleware!')
console.dir(context)
}
/pages/index.vue
<template>
<div class="container">
<section>
<Counter />
</section>
</div>
</template>
<script>
export default {
middleware: 'sampleMiddleware',
}
</script>
layout
Webページのレイアウトを定義できる。pagesディレクトリ内の.vueでどのレイアウトを使用するか定義できる。middlewareと組み合わせてあれこれできそう。
/layout/default.vue
<template>
<div>
<h1>default.vue</h1>
<Nuxt />
</div>
</template>
<style>
:
</style>
/layout/sampleLayout.vue
<template>
<div>
<h1>sampleLayout.vue</h1>
<Nuxt />
</div>
</template>
<style>
:
</style>
/pages/index.vue
<template>
<div class="container">
<section>
<Counter />
</section>
</div>
</template>
<script>
export default {
layout: 'sampleLayout', // switch layout
middleware: 'sampleMiddleware',
}
</script>
<style scoped>
:
</style>
おわり
一息入れたらnamespaceやmodulesも触っておこうと思います。