369 lines
10 KiB
Markdown
369 lines
10 KiB
Markdown
|
+++
|
||
|
date = "2018-07-26"
|
||
|
tags = ["vue"]
|
||
|
title = "vue+hugo+milligramでブログを作ってみた"
|
||
|
slug = "vue"
|
||
|
+++
|
||
|
|
||
|
## 導入
|
||
|
|
||
|
最近、フロントエンドに`vue.js`を使うことが多くなりました。
|
||
|
|
||
|
とは言え、私は、コンピュータ関連のことは何も知らないのに、カッコつけてそれらしいキーワードを並び立ててるに過ぎないんですけどね。フロントエンドとか`vue.js`とか、いかにもって感じじゃないですか。前回書いた記事も本当はそんな感じの内容なんですよね。多分、分かる人には「あ、こいつそれらしいキーワードを並び立ててるだけで、本当はコンピュータ、プログラミングのことをまるで分かっていない初心者だな」と見抜かれてしまうと思うんですけどね...。
|
||
|
|
||
|
さて、前置きはそのくらいにして、この前、`vue.js`という「それっぽいやつ」をなんとなしに使ってみたのですが、所感としては、`vue.js`かなりいいです!
|
||
|
|
||
|
で、これは何かと言うと、`react`, `angular`みたいなやつです、Webフレームワーク。
|
||
|
|
||
|
ということで、今回は、どんな感じで`vue`を使ってみたのかの記録になっています。
|
||
|
|
||
|
### vueは何がいいのか
|
||
|
|
||
|
あ、いつの間にか名前を小文字にしてしまってる。本当はVue.jsなのかな。知らん。
|
||
|
|
||
|
しかし、やっぱり基本的には全部小文字で書きたいので、今後は小文字でいきます。
|
||
|
|
||
|
いきなり話がそれましたが、`vue`は何がいいのかと言うと、私の感想では、「簡単に作れる」という点がいいなと思っています。それほど複雑でもないし。
|
||
|
|
||
|
ですが、大規模なものを`vue`で作っていくと、スケールしにくいんじゃないかなって感じはします。一方、`react`とかだと最初は複雑だったり、難しかったりするかもですが、スケールしやすいのではないかな。
|
||
|
|
||
|
とは言え、かなり大雑把な感想ですが、私は、vueのほうが好きですね。しかし、内容によって使い分けたほうが良いと思います。
|
||
|
|
||
|
## サンプルコード
|
||
|
|
||
|
そんな`vue`ですが、実際にどう使ったのかと言うと、jsonを読み込んでページに反映するみたいな感じのものを書きました。
|
||
|
|
||
|
> package.json
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"name": "vue-sample",
|
||
|
"version": "0.0.1",
|
||
|
"author": "syui <syui@syui.cf>",
|
||
|
"license": "MIT",
|
||
|
"scripts": {
|
||
|
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot --host 0.0.0.0 --port 7000",
|
||
|
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
|
||
|
"build:watch": "cross-env NODE_ENV=production WEBPACK_WATCH=true webpack --progress --hide-modules"
|
||
|
},
|
||
|
"dependencies": {
|
||
|
"vue": "^2.4.2",
|
||
|
"vuex": "^2.4.0"
|
||
|
},
|
||
|
"devDependencies": {
|
||
|
"axios": "^0.17.1",
|
||
|
"babel-core": "^6.0.0",
|
||
|
"babel-loader": "^6.0.0",
|
||
|
"babel-preset-latest": "^6.0.0",
|
||
|
"copy-webpack-plugin": "^4.0.1",
|
||
|
"cross-env": "^3.0.0",
|
||
|
"css-loader": "^0.28.7",
|
||
|
"extract-text-webpack-plugin": "^2.1.0",
|
||
|
"file-loader": "^0.11.2",
|
||
|
"html-webpack-plugin": "^2.28.0",
|
||
|
"postcss": "^6.0.11",
|
||
|
"postcss-base64": "^0.7.1",
|
||
|
"postcss-cssnext": "^3.0.2",
|
||
|
"postcss-loader": "^2.0.6",
|
||
|
"postcss-smart-import": "^0.7.5",
|
||
|
"postcss-url": "^7.1.2",
|
||
|
"style-loader": "^0.18.2",
|
||
|
"vue-loader": "^12.0.4",
|
||
|
"vue-template-compiler": "^2.4.2",
|
||
|
"webpack": "^2.6.1",
|
||
|
"webpack-dev-server": "2.7.1"
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
> webpack.config.js
|
||
|
|
||
|
```sh
|
||
|
var path = require('path');
|
||
|
var webpack = require('webpack');
|
||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||
|
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||
|
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
||
|
|
||
|
module.exports = {
|
||
|
watch: process.env.WEBPACK_WATCH === 'true',
|
||
|
entry: './src/main.js',
|
||
|
output: {
|
||
|
path: path.resolve(__dirname, './public-vue'),
|
||
|
publicPath: '',
|
||
|
filename: 'build.js'
|
||
|
},
|
||
|
module: {
|
||
|
rules: [
|
||
|
{
|
||
|
test: /\.vue$/,
|
||
|
loader: 'vue-loader',
|
||
|
options: {
|
||
|
loaders: {
|
||
|
}
|
||
|
// other vue-loader options go here
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
test: /\.js$/,
|
||
|
loader: 'babel-loader',
|
||
|
exclude: /node_modules/
|
||
|
},
|
||
|
{
|
||
|
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
|
||
|
loader: 'file-loader',
|
||
|
options: {
|
||
|
name: '[name].[ext]?[hash]'
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
test: /\.css$/,
|
||
|
loader: ExtractTextPlugin.extract({
|
||
|
fallback: 'style-loader',
|
||
|
use: [
|
||
|
{
|
||
|
loader: 'css-loader',
|
||
|
options: { importLoaders: 1 }
|
||
|
},
|
||
|
{
|
||
|
loader: 'postcss-loader',
|
||
|
options: {
|
||
|
plugins: [
|
||
|
require('postcss-smart-import')(),
|
||
|
require('postcss-url')(),
|
||
|
require('postcss-base64')({
|
||
|
extensions: ['.svg'],
|
||
|
root: 'src'
|
||
|
}),
|
||
|
require('postcss-cssnext')({
|
||
|
browsers: ['> 1%', 'last 2 versions', 'Firefox ESR', 'Opera 12.1'],
|
||
|
features: { autoprefixer: { remove: false } } // 'background-image: radial-gradient' broken in autoprefixer
|
||
|
})
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
})
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
plugins: [
|
||
|
new ExtractTextPlugin('[name].css'),
|
||
|
new HtmlWebpackPlugin({
|
||
|
template: 'src/index.html'
|
||
|
})
|
||
|
],
|
||
|
resolve: {
|
||
|
alias: {
|
||
|
'vue$': 'vue/dist/vue.esm.js'
|
||
|
}
|
||
|
},
|
||
|
devServer: {
|
||
|
historyApiFallback: true,
|
||
|
noInfo: true
|
||
|
},
|
||
|
performance: {
|
||
|
hints: false
|
||
|
},
|
||
|
devtool: '#eval-source-map'
|
||
|
};
|
||
|
|
||
|
if (process.env.NODE_ENV === 'production') {
|
||
|
module.exports.devtool = '#source-map';
|
||
|
// http://vue-loader.vuejs.org/en/workflow/production.html
|
||
|
module.exports.plugins = (module.exports.plugins || []).concat([
|
||
|
new webpack.DefinePlugin({
|
||
|
'process.env': {
|
||
|
NODE_ENV: '"production"'
|
||
|
}
|
||
|
}),
|
||
|
new webpack.optimize.UglifyJsPlugin({
|
||
|
sourceMap: true,
|
||
|
compress: {
|
||
|
warnings: false
|
||
|
}
|
||
|
}),
|
||
|
new webpack.LoaderOptionsPlugin({
|
||
|
minimize: true
|
||
|
})
|
||
|
]);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
ここでビルドやプレビューに必要なものをインストールします。
|
||
|
|
||
|
```sh
|
||
|
$ yarn install
|
||
|
```
|
||
|
|
||
|
インストールできたら先程作った`webpack`の設定ファイルに沿って、vueのコードを書いていきます。
|
||
|
|
||
|
> src/main.js
|
||
|
|
||
|
```
|
||
|
import Vue from 'vue'
|
||
|
import axios from 'axios'
|
||
|
|
||
|
Vue.component('demo-grid', {
|
||
|
template: '#grid-template',
|
||
|
props: {
|
||
|
data: Array,
|
||
|
columns: Array,
|
||
|
filterKey: String
|
||
|
},
|
||
|
data: function () {
|
||
|
var sortOrders = {}
|
||
|
this.columns.forEach(function (key) {
|
||
|
sortOrders[key] = 1
|
||
|
})
|
||
|
return {
|
||
|
sortKey: '',
|
||
|
sortOrders: sortOrders
|
||
|
}
|
||
|
},
|
||
|
computed: {
|
||
|
filteredData: function () {
|
||
|
var sortKey = this.sortKey
|
||
|
var filterKey = this.filterKey && this.filterKey.toLowerCase()
|
||
|
var order = this.sortOrders[sortKey] || 1
|
||
|
var data = this.data
|
||
|
if (filterKey) {
|
||
|
data = data.filter(function (row) {
|
||
|
return Object.keys(row).some(function (key) {
|
||
|
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
if (sortKey) {
|
||
|
data = data.slice().sort(function (a, b) {
|
||
|
a = a[sortKey]
|
||
|
b = b[sortKey]
|
||
|
return (a === b ? 0 : a > b ? 1 : -1) * order
|
||
|
})
|
||
|
}
|
||
|
return data
|
||
|
}
|
||
|
},
|
||
|
filters: {
|
||
|
capitalize: function (str) {
|
||
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
sortBy: function (key) {
|
||
|
this.sortKey = key
|
||
|
this.sortOrders[key] = this.sortOrders[key] * -1
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
|
||
|
var status = new Vue({
|
||
|
el: '#status',
|
||
|
data: {
|
||
|
gridColumns: ['name','team','xp','get','zukan'],
|
||
|
gridData: []
|
||
|
},
|
||
|
beforeCreate: function () {
|
||
|
axios.get('https://syui.gitlab.io/pokemon-zukan/json/status.json')
|
||
|
.then(function (response) {
|
||
|
status.gridData = response.data;
|
||
|
})
|
||
|
.catch(function (error) {
|
||
|
console.log(error);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
```
|
||
|
|
||
|
> src/index.html
|
||
|
|
||
|
```
|
||
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
<head>
|
||
|
<meta charset="utf-8">
|
||
|
<title>title</title>
|
||
|
<style>
|
||
|
table {
|
||
|
border: 2px solid #42b983;
|
||
|
border-radius: 3px;
|
||
|
background-color: #fff;
|
||
|
}
|
||
|
|
||
|
th {
|
||
|
background-color: #42b983;
|
||
|
color: rgba(255,255,255,0.66);
|
||
|
cursor: pointer;
|
||
|
-webkit-user-select: none;
|
||
|
-moz-user-select: none;
|
||
|
-ms-user-select: none;
|
||
|
user-select: none;
|
||
|
}
|
||
|
|
||
|
td {
|
||
|
background-color: #f9f9f9;
|
||
|
}
|
||
|
|
||
|
th, td {
|
||
|
min-width: 120px;
|
||
|
padding: 10px 20px;
|
||
|
}
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
|
||
|
<script type="text/x-template" id="grid-template">
|
||
|
<table>
|
||
|
<thead>
|
||
|
<tr>
|
||
|
<th v-for="key in columns"
|
||
|
@click="sortBy(key)"
|
||
|
:class="{ active: sortKey == key }">
|
||
|
{{ key | capitalize }}
|
||
|
<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
|
||
|
</span>
|
||
|
</th>
|
||
|
</tr>
|
||
|
</thead>
|
||
|
<tbody>
|
||
|
<tr v-for="entry in filteredData">
|
||
|
<td v-for="key in columns">
|
||
|
{{entry[key]}}
|
||
|
</td>
|
||
|
</tr>
|
||
|
</tbody>
|
||
|
</table>
|
||
|
</script>
|
||
|
|
||
|
<div id="status">
|
||
|
<demo-grid
|
||
|
:data="gridData"
|
||
|
:columns="gridColumns">
|
||
|
</demo-grid>
|
||
|
</div>
|
||
|
|
||
|
</body>
|
||
|
</html>
|
||
|
```
|
||
|
|
||
|
あとは、プレビューで確認してみましょう。
|
||
|
|
||
|
```sh
|
||
|
$ yarn dev
|
||
|
```
|
||
|
|
||
|
## hugo+milligram
|
||
|
|
||
|
この`vue`と静的サイトジェネレーターである`hugo`、cssフレームワークである`milligram`を組み合わせて、簡単にブログを作ってみました。
|
||
|
|
||
|
何でかと言うと、`vue`って、トップページやWebアプリを作るのには向いていると思うんですが、ブログを作るのには向いてないと思うんですよね。まず、各種ページを作ってmarkdownで書く体裁を作るのがめんどくさそう。ということで、ここは慣れたhugoを使うことに。あと、cssも書くの面倒ですからね。milligramというやつを使って設定することに。
|
||
|
|
||
|
あ、組み合わせに関しては、`yarn build`して`./public-vue/build*`を`hugo`の`./static/`に置いて、適時、もしくは`head`とかに`<script type="text/javascript" src="build.js"></script>`みたいな感じで読み込めばいいと思います。
|
||
|
|
||
|
## 感想
|
||
|
|
||
|
ほとんどサンプルなんですけどね、ざっとコードを見てみた感じで言いますと、vueはわかりやすいし、作りやすい感じしますね。
|
||
|
|
||
|
なので、フロントエンドの初学者には、`vue.js`はおすすめかもです。
|
||
|
|