今回はnodeサーバーもVuexもNuxt.jsも使いません。
細かい所いろいろ抜けてる気がするけどとりあえずこんな感じでできるのかという検証途中のメモ代わりに。
非同期通信で取得したコンテンツをコマンドライン上でSSRで描画するまで(今回はルーティングなしの単一ページのみ)
Vueは初心者なのでもしかしたら結構悪い例かも。
Webpackの設定
webpack.config.js
const webpack = require("webpack");
module.exports = [{
target: 'node',
context: __dirname + '/source',
entry: {
'application': './es2015/application',
},
output: {
path: __dirname + '/distribution/javascript',
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue-loader',
exclude: /node_modules/
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
query:{
presets: ['es2015']
}
}
]
},
}];
実行ソース
store.js
// 今回はaxiosを利用
import axios from 'axios';
// storeオブジェクト、公式ではVuexが推奨されているっぽい。
export default {
items: [],
prefetch() {
var store = this;
//promiseを返す
//{ items: [ 'foo', 'bar', 'baz' ] }が帰ってくるURL
return axios.get('http://XXXXXXX.com/items')
.then(res => {
store.items = res.data.items;
});
}
}
list.vue
<template>
<ul>
<li v-for="item in items">{{item}}</li>
</ul>
</template>
<script>
import store from './store';
export default {
data() { return store; }
}
</script>
application.js
import Vue from 'vue'
import list from './list.vue';
import store from './store';
const app = new Vue({
render: h => h(list)
});
const renderer = require('vue-server-renderer').createRenderer();
//storeオブジェクトにjsonを格納後、レンダリングを実行。
store.prefetch()
.then(()=>{
renderer.renderToString(app, (err, html) => {
if (err) throw err
console.log(`<!DOCTYPE html>
<html lang="ja">
<head><title>Hello</title></head>
<body>
${html}
</body>
</html>`)
})
});
#build実行
webpack
#実行
node bundle.js
#実行結果
<!DOCTYPE html>
<html lang="ja">
<head><title>Hello</title></head>
<body>
<ul data-server-rendered="true"><li>foo</li><li>bar</li><li>baz</li></ul>
</body>
</html>
所感まとめ
- 当然だけど非同期通信で取得するコンテンツについてはレンダリング実行前にプリフェッチしておく必要があるみたい。
- たったこれだけでもサンプルソース少なくて意外とハマった。
- 最近はSSR不要論とかも聞くようになってきたけどTwitterカードやはてなブックマークは未対応なのでまだまだSPAオンリーというわけには行かないのかなぁという印象。
- ただがっつりSSRやるってなると結構工数や考慮点が重くなりそうなので正直個人開発とかの範囲ではあんまりやりたくはない。
参考
ちゃんとSSRやりたい場合はこの辺参照