nodeがサーバサイドでレンダリングされたhtmlを返すためにレンダラーを使用する場合、nodeアプリケーションで事前に作成されたアプリを渡すことになり、以下のような問題が発生します。
const app = new Vue({ ... })
app.get('*', (req, res) => {
renderer.renderToString(app, (err, html) => {
...
})
}
誰がこのファイルをリクエストしても、実際に入ってくるのはアプリで、それはみんなに共有されていることに気づいたことがありますか?ページをリクエストした王馬子がアプリに変更を加えた場合、その後にページをリクエストした人は何らかの影響を受けることになります。ステートフルシングルトンは避けるべきです。
したがって、アプリケーションインスタンスを直接作成する代わりに、リクエストごとに新しいアプリケーションインスタンスを作成するために繰り返し実行できるファクトリ関数を公開する必要があります:
// app.js
const Vue = require('vue')
module.exports = function createApp (context) {
return new Vue({
data: {
url: context.url
},
template: `<div>アクセスするためのURLは {{ url }}</div>`
})
}
// server.js
const createApp = require('./app')
app.get('*', (req, res) => {
const app = createApp({ url: req.url })
renderer.renderToString(app, (err, html) => {
// エラーを処理する
res.end(html)
})
})
ルーター、ストア、イベント・バス・インスタンスにも同じルールが適用されます。
詳しくは、同じwebpackの2つのバージョンを同時にビルドする方法をご覧ください。
プリレンダリングにはVueインスタンスが必要であること、クライアントには実際にVueインスタンスが必要であること。多くの場合、何らかの区別が必要です。
// シングルページのアプリケーションを書く場合、Vueインスタンスはメインの.jsを直接マウントする
// これがwebpackの書き方に基づいていることを知る。
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')
// 必要なのは2つのアプリインスタンスで、1つはサーバーサイドレンダリング用、もう1つはクライアントサイド用だ。.jsとなるように修正する。
export function createApp () {
const app = new Vue({
// ルートインスタンスは、単にアプリケーションコンポーネントをレンダリングする。
render: h => h(App)
})
return { app }
}
// インスタンスを生成するのはメソッド関数であることがわかるだろう。
// クライアント側で使用する必要がある場合は、インスタンスを作成してマウントするだけだ!
import { createApp } from './app'
const { app } = createApp()
app.$mount('#app')
// サーバ上でレンダリングする場合は、アプリのインスタンス
import { createApp } from './app'
export default context => {
const { app } = createApp()
return app
}