2018/10/27 追記
store を使うよう更新しました。
詳細はこちらを参照してください。
これまで 2 回に渡って nuxt と element-ui の使い方をみてきました。
これらを踏まえ、今日は実際に Qiita 投稿一覧サイトを作っていきたいと思います。
最初に考えた要件では以下の 3 点を挙げていました。
- トップページから Qiita の投稿一覧ページへ移行させる
- Qiita の投稿一覧ページではヘッダ有りとする
- UI としては Element を利用する
これに以下 2 点を加えます。
モジュールのバージョン
使用するモジュールのバーションは以下の通りです。
1 2 3
| "nuxt": "^1.0.0-rc11", "axios": "^0.17.1", "element-ui": "^2.0.8"
|
プロジェクトの作成
新たにプロジェクトを作成します。
プロジェクト名は「hello-nuxt」としました。
1 2 3 4
| $ vue init nuxt-community/starter-template hello-nuxt $ cd hello-nuxt $ yarn install $ yarn run dev
|
axios、element-ui のインストール
axios、element-ui をインストールし、設定を行います。
1
| $ yarn add axios element-ui
|
nuxt.config.js の build 内に以下の設定を追加します。
1 2
| build: { vendor: ['axios', 'element-ui'],
|
nuxt.config.js の plugins、css に以下の設定を追加します。
1 2 3 4
| plugins: ['~plugins/element-ui', { src: '~plugins/element-ui', ssr: false }], css: [ 'element-ui/lib/theme-chalk/index.css' ],
|
/plugins/element-ui.js を作成します。
トップページの作成
/pages/index.vue の内容を変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <template> <section class="container"> <div> <h1 class="title"> Qiita </h1> <h4 class="subtitle"> Qiita is a technical knowledge sharing and collaboration platform for programmers. </h4> <div class="links"> <nuxt-link to="/search" class="button--white"><i class="el-icon-search"></i> Search</nuxt-link> </div> </div> </section> </template>....以下略
|
Qiita の投稿一覧ページ用のヘッダー、フッター作成
components/Header.vue を作成します。
1 2 3 4 5
| <template> <div class="header"> <b><nuxt-link to="/">Hello Qiita with Nuxt.js \\\\ ٩(*'ω'*)و ////</nuxt-link></b> </div> </template>....以下略
|
components/Footer.vue を作成します。
1 2 3 4
| <template> <div class="footer"> </div> </template>....以下略
|
続いて layouts/navbar.vue を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <template> <div> <my-header /> <nuxt /> <my-footer /> </div> </template>
<script> import MyHeader from '~/components/Header.vue' import MyFooter from '~/components/Footer.vue' export default { name: 'navbar', components: { MyHeader, MyFooter } } </script>
|
Qiita の投稿一覧ページの作成
検索部の作成
pages/search.vue を作成します。
ここではページ表示時に Qiita の API をコールする機能と
検索画面に入力されたキーワードを元に Qiita の API をコールする機能、
入力値のバリデーション機能を持ちます。
エンターキー押下で検索が走るようにもします。
投稿(Qiita API のレスポンスデータ)を描画する部分は別途作ります(コード上では my-list が当該箇所)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| <template> <div> <el-container> <el-main> <el-form :inline="true" :model="searchForm" ref="searchForm" :rules="rules" @submit.native.prevent> <el-form-item prop="keyword"> <el-input placeholder="search by keyword" prefix-icon="el-icon-search" v-model="searchForm.keyword"@keyup.enter.native="search('searchForm')" /> </el-form-item> <el-form-item> <el-button @click="search('searchForm')">search</el-button> </el-form-item> </el-form> <my-list :lists="mylist" :hasData="hasData" /> </el-main> </el-container> </div> </template>
<script lang="babel"> import axios from 'axios' import MyList from '~/components/List.vue' const BASE_URL = 'https://qiita.com/api/v2/' export default { layout: 'navbar', components: { MyList }, data () { return { searchForm: { keyword: '' }, rules: { keyword: [ { required: true, message: 'Please input the keyword', trigger: 'blur' } ] }, mylist: [], hasData: true } }, created () { this.searchForm.keyword = 'nuxt.js' this.sendRequest() this.searchForm.keyword = '' }, methods: { search (form) { this.$refs[form].validate((valid) => { if (!valid) { return false } this.sendRequest() }) }, sendRequest () { axios.get(BASE_URL+ 'items', { headers: {'Content-Type': 'application/json'}, params: { page: 1, per_page: 20, query: this.searchForm.keyword } }) .then(response => { if (response.data.length === 0) { this.hasData = false } this.mylist = response.data }) .catch(e => { console.error('error:', e) }) } } } </script>....以下略
|
投稿一覧部の作成
components/List.vue を作成します。
ここでは search.vue で取得した Qiita API のレスポンスデータを受け取り描画する機能と
トップへスクロールする機能を持ちます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| <template> <div> <div v-if="lists.length === 0 && !hasData"> <i class="el-icon-warning"> No results found for your keyword.</i> </div> <div v-else> <el-col :span="6" v-for="(element, index) in lists" :key="index" class="col-style"> <el-card :body-style="{ padding: '15px' }" class="box-card"> <div slot="header" class="clearfix"> <a :href="element.url" target="_blank">{{ element.title }}</a> </div> <div class="bottom clearfix content-style text"> <div>{{ element.created_at }}</div> <span> <img :src="element.user.profile_image_url" width="15" height="15" /> <template v-if="element.user.description"> <el-popover slot="description" placement="top-start" width="300" trigger="hover" :content="element.user.description"> <span slot="reference"> {{ element.user.id }}</span> </el-popover> </template> <template v-else> <span> {{ element.user.id }}</span> </template> </span> <span> <i class="el-icon-star-off">{{ element.likes_count }}</i> </span> <div>{{ getDescription(element.body) }}</div> <el-tag size="mini" type="info" class="tab-style" v-for="(tag, index) in element.tags" :key="index">{{ tag.name }}</el-tag> </div> </el-card> </el-col> <div v-if="250 < scrollY" class="page-component-up"> <transition name="fade"> <i class="el-icon-caret-top" @click="scrollTop" /> </transition> </div> </div> </div> </template>
<script lang="babel"> export default { props: ['lists', 'hasData'], data () { return { scrollY: 0 } }, mounted () { window.addEventListener('scroll', this.handleScroll) }, methods: { getDescription: function (body) { return body.slice(0, 100) + '...' }, handleScroll: function () { this.scrollY = window.scrollY }, scrollTop: function () { document.body.scrollTop = 0 document.documentElement.scrollTop = 0 } } } </script>....以下略
|
ページネーションの作成
続いて、ページネーションを作成しようと思いした…が
レスポンスヘッダーに含まれている「Total-Count」が取得できないため今回は断念しました。

デモ
https://aytdm.github.io/hello-nuxt/
今回は GitHub Pages にデプロイしました。
※PC での閲覧推奨です。
まとめ
コンポーネント間のデータの受け渡しやリスト、イベントハンドリングなども行うことができ、
基本的な操作は体験できたように思います:)
#ストアやトランジションはまたの機会に…!
デモで分かるようにレスポンシブではなく、スマホだととても見れないので
別の形式で表示するか、element-ui 以外の UI ライブラリを選定した方が良さそうです。
この点を考慮していませんでした。。。
テスト周りは追々勉強&追加していこうと思います。
vue.js は画面で行われる処理が分かりやすくていいですね。
使える機会がある際は積極的に使っていきたいと思います。
ソースコード
v1.0
参考