express + webpack + VueでSSRする
express + webpack + VueでSSRする
以前にexpressとVueのみを用いて行ったSSRはシンプルに実装できましたが、今回webpackを組み合わせたSSRはファイル数も増え、仕組みも複雑になってきました。 今回はSSR環境構築の足場を作り、次はホットリロードなども実装して仕組みの解説も行いたいと思ます。
手を動かす
以下が手順概要になります。
- npmでプロジェクトを作成する
- express、webpack、Vue等をインストールする
- 必要なファイルを手動で作成する
- コマンドラインでからwebpackを起動し、ビルドする
- コマンドラインからWEBサーバー(express)を立ち上げる
- ブラウザでアクセスして確認する
1. npmでプロジェクトを作成する
以下のコマンドを実行します。
mkdir express-webpack-vue-ssr-demo cd express-webpack-vue-ssr-demo npm init -y
2. express、webpack、Babel、Vue等をインストールする
npm install -D webpack webpack-cli webpack-merge friendly-errors-webpack-plugin npm install -S express vue vue-server-renderer
3. 必要なファイルを手動で作成する
- ./index.template.html
- ./server.js
- ./webpack.base.config
- ./webpack.client.config
- ./webpack.server.config
- ./src/App.vue
- ./src/app.js
- ./src/entry-client.js
- ./src/entry-server.js
package.json
{ "name": "express-webpack-babel-vue-ssr-demo", "version": "1.0.0", "description": "", "private": true, "scripts": { "dev": "node server", "build": "webpack --config=webpack.server.config.js && webpack --config=webpack.client.config.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "friendly-errors-webpack-plugin": "^1.7.0", "vue-loader": "^15.7.0", "vue-template-compiler": "^2.6.10", "webpack": "^4.29.6", "webpack-cli": "^3.3.0", "webpack-merge": "^4.2.1" }, "dependencies": { "express": "^4.16.4", "vue": "^2.6.10", "vue-server-renderer": "^2.6.10" } }
./index.template.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>express + webpack + Vue + SSR</title> <script src="dist/main.js" defer></script> </head> <body> <!--vue-ssr-outlet--> </body> </html>
./server.js
const path = require('path'); const fs = require('fs'); const express = require('express'); const VueServerRenderer = require('vue-server-renderer'); const app = express(); app.use('/dist', express.static('dist')); app.use(express.static(__dirname)); const template = fs.readFileSync('./index.template.html', 'utf-8'); const renderer = VueServerRenderer.createBundleRenderer(path.join(__dirname, 'dist/vue-ssr-server-bundle.json'), { template }); app.get('*', (req, res) => { const ctx = { url: req.url }; renderer.renderToString(ctx, (err, html) => { res.end(html); }); }); app.listen(8080, () => { console.log(`Server listening on http://localhost:8080, Ctrl+C to stop`); });
./webpack.base.config.js
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin'); module.exports = { devtool: 'inline-source-map', output: { filename: '[name].js', path: path.join(__dirname, 'dist') }, module: { rules: [ { test: /\.vue/, exclude: /node_modules/, use: ['vue-loader'] } ] }, plugins: [ new VueLoaderPlugin(), new FriendlyErrorsWebpackPlugin() ] };
./webpack.client.config.js
const baseConfig = require('./webpack.base.config.js'); const merge = require('webpack-merge'); const path = require('path'); module.exports = merge(baseConfig, { entry: path.join(__dirname, 'src/entry-client.js') });
./webpack.server.config.js
const path = require('path'); const VueSSRServerPlugin = require('vue-server-renderer/server-plugin'); const baseConfig = require('./webpack.base.config.js'); const merge = require('webpack-merge'); module.exports = merge(baseConfig, { entry: path.join(__dirname, './src/entry-server.js'), target: 'node', output: { libraryTarget: 'commonjs2' }, plugins: [ new VueSSRServerPlugin() ] });
./src/App.vue
<template> <div id="app"> <div><input v-model="text" type="text"></div> {{ text }} </div> </template> <script> export default { data() { return { text: 'Hello express + webpack + Vue + SSR!!' } } } </script> <style> </style>
./src/app.js
import Vue from 'vue'; import App from './App.vue'; export default function createApp() { return new Vue({ render: h => h(App) }); }
./src/entry-client.js
import createApp from './app'; const app = createApp(); app.$mount('#app'); window.myApp = app;
./src/entry-server.js
import createApp from './app'; export default ctx => { return new Promise((resolve, reject) => { const app = createApp(); resolve(app); }); }
4. コマンドラインでからwebpackを起動し、ビルドする
以下のコマンドを実行します。
npm run build
5. コマンドラインからWEBサーバー(express)を立ち上げる
以下のコマンドを実行します
npm run dev
5. ブラウザでアクセスして確認する
以下のURLにブラウザでアクセスします。
http://localhost:8080/
表示されたフォームの値と、表示されているテキストが連動して変更されれば成功です。