Nuxt.jsビギナーズガイド
posted with ヨメレバ
花谷拓磨 シーアンドアール研究所 2018年10月
サンプルアプリの作成(続き)
ログイン情報の永続化
1. サーバサイドでもCookieを扱うことができるuniversal-cookieを導入
npm install universal-cookie
2. app/pages/index.vueにCookieへの書き込み追加
import Cookies from 'universal-cookie'
で読み込み。const cookies = new Cookies()
でインスタンス生成。cookies.set('user', JSON.stringify(this.user))
で保存。
<template>
<section class="container">
<el-card style="flex: 1">
<div slot="header" class="clearfix">
<span>ログイン</span>
</div>
<form>
<div class="form-content">
<span>ユーザー ID</span>
<el-input placeholder="" v-model="formData.id"/>
</div>
<div class="form-content">
<el-checkbox v-model="isCreateMode">アカウントを作成する</el-checkbox>
</div>
<div class="text-right">
<el-button type="primary" @click="handleClickSubmit">{{ buttonText }}</el-button>
</div>
</form>
</el-card>
</section>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import Cookies from 'universal-cookie'
export default {
asyncData ({ redirect, store }) {
if (store.getters.user) {
redirect('/posts/')
}
return {
isCreateMode: false,
formData: {
id: ''
}
}
},
computed: {
buttonText () {
return this.isCreateMode ? '新規登録' : 'ログイン'
},
...mapGetters(['user'])
},
methods: {
async handleClickSubmit () {
const cookies = new Cookies()
if (this.isCreateMode) {
try {
await this.register({ ...this.formData })
this.$notify({
type: 'success',
title: 'アカウント作成完了',
message: `${this.formData.id} として登録しました`,
position: 'bottom-right',
duration: 1000
})
cookies.set('user', JSON.stringify(this.user))
this.$router.push('/posts/')
} catch (e) {
this.$notify.error({
title: 'アカウント作成失敗',
message: '既に登録されているか、不正なユーザー ID です',
position: 'bottom-right',
duration: 1000
})
}
} else {
try {
await this.login({ ...this.formData })
this.$notify({
type: 'success',
title: 'ログイン成功',
message: `${this.formData.id} としてログインしました`,
position: 'bottom-right',
duration: 1000
})
cookies.set('user', JSON.stringify(this.user))
this.$router.push('/posts/')
} catch (e) {
this.$notify.error({
title: 'ログイン失敗',
message: '不正なユーザー ID です',
position: 'bottom-right',
duration: 1000
})
}
}
},
...mapActions(['login', 'register'])
}
}
</script>
<style scoped>
.form-content {
margin: 16px 0;
}
</style>
3. mkdir app/middleware; touch app/middleware/auth-cookie.js
import Cookies from 'universal-cookie'
export default ({ req, store }) => {
if (process.browser) {
return
}
const cookies = new Cookies(req.headers.cookie)
const user = cookies.get('user')
if (user && user.id) {
const { id, likes } = user
store.commit('setUser', { user: { id, likes } })
}
}
4. nuxt.config.jsに登録
router: {
middleware: [
'auth-cookie',
]
},
5. app/components/TheHeader.vueにログイン中のユーザー情報表示を追加
<template>
<el-menu mode="horizontal" :router="true">
<el-menu-item index="1" style="pointer-events:none;">
Nuxt Diary App
</el-menu-item>
<el-menu-item index="2" :route="{ path: '/posts/' }">
投稿一覧
</el-menu-item>
<no-ssr>
<el-menu-item index="4" style="float: right;" :route="{ path: `/users/${user.id}` }" v-if="user">
<span>{{user.id}}</span>
</el-menu-item>
<el-menu-item index="4" style="float: right;" :route="{ path: topLink }">
<span>ログイン</span>
</el-menu-item>
</no-ssr>
<el-menu-item index="5" style="float: right" :route="{ path: '/posts/new' }">
新規投稿
</el-menu-item>
</el-menu>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
topLink () {
if (this.$store.getters.user) {
return '/posts/'
}
return '/'
},
...mapGetters(['user'])
}
}
</script>
投稿機能の実装
1. momentの導入
npm install moment
touch app/plugins/moment.js
import 'moment/locale/ja'
import moment from 'moment'
moment.locale('ja')
export default moment
2. vuexのpostsモジュールを作成
touch app/store/posts.js
import moment from '~/plugins/moment'
export const state = () => ({
posts: []
})
export const getters = {
posts: state => state.posts
}
export const mutations = {
addPost (state, { post }) {
state.posts.push(post)
},
updatePost (state, { post }) {
state.posts = state.posts.map(p => (p.id === post.id ? post : p))
},
clearPosts (state) {
state.posts = []
}
}
export const actions = {
async publishPost ({ commit }, { payload }) {
const post = { ...payload, created_at: moment().format() }
// 書籍ではfirebaseに登録しているが、ここでは何もしない
await this.$axios.$put('', [post])
commit('addPost', post)
}
}
3. 投稿ページの作成
touch app/pages/posts/new.vue
<template>
<section class="container posts-page">
<el-card style="flex: 1">
<div slot="header" class="clearfix">
<el-input placeholder="タイトルを入力" v-model="formData.title"/>
</div>
<div>
<el-input placeholder="本文を入力……" type="textarea" rows="15" v-model="formData.body"/>
</div>
<div class="text-right" style="margin-top: 16px;">
<el-button type="primary" @click="publish" round>
<span class="el-icon-upload2"/>
<span>Publish</span>
</el-button>
</div>
</el-card>
</section>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
asyncData ({ redirect, store }) {
if (!store.getters.user) {
redirect('/')
}
return {
formData: {
title: '',
body: ''
}
}
},
computed: {
...mapGetters(['user'])
},
methods: {
async publish () {
await this.publishPost({
payload: {
user: this.user,
...this.formData
}
})
await this.$router.push('/posts')
},
...mapActions('posts', ['publishPost'])
}
}
</script>
<style>
.posts-page .el-table__row {
cursor: pointer;
}
</style>
投稿一覧を非同期で取得するように変更
1. 書籍ではfirebaseを使っているが、ここではローカルにjsonを用意する。
touch static/posts.json
[
{
"id": "001",
"title": "テストタイトル",
"body": "ここに本文が入ります。",
"created_at": "2021/10/30 06:00:00",
"user": {
"id": "hoge"
}
},
{
"id": "002",
"title": "テストタイトル2",
"body": "ここに本文2が入ります。",
"created_at": "2021/10/31 06:30:00",
"user": {
"id": "hoge2"
}
}
]
2. app/store/index.jsのactionsにfetchPostsを追加
async fetchPosts ({ commit }) {
const posts = await this.$axios.$get('/posts.json')
commit('clearPosts')
posts.forEach(content =>
commit('addPost', {
post: {
...content
}
})
)
},
3. app/pages/posts/index.vueを修正
import { mapGetters } from 'vuex'
import moment from '~/plugins/moment'
export default {
async asyncData ({ store }) {
await store.dispatch('posts/fetchPosts')
},
computed: {
showPosts () {
return this.posts.map((post) => {
post.created_at = moment(post.created_at).format('YYYY/MM/DD HH:mm:ss')
return post
})
},
...mapGetters('posts', ['posts'])
}
}