2018-06-07 14:59:05 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the pig4cloud.com developer nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived from
|
|
|
|
* this software without specific prior written permission.
|
|
|
|
* Author: lengleng (wangiegie@gmail.com)
|
|
|
|
*/
|
|
|
|
|
2018-03-04 13:49:35 +08:00
|
|
|
const fs = require('fs')
|
|
|
|
const path = require('path')
|
|
|
|
const LRU = require('lru-cache')
|
|
|
|
const express = require('express')
|
|
|
|
const favicon = require('serve-favicon')
|
|
|
|
const compression = require('compression')
|
|
|
|
const microcache = require('route-cache')
|
|
|
|
const resolve = file => path.resolve(__dirname, file)
|
|
|
|
const { createBundleRenderer } = require('vue-server-renderer')
|
|
|
|
|
|
|
|
const isProd = process.env.NODE_ENV === 'production'
|
|
|
|
const useMicroCache = process.env.MICRO_CACHE !== 'false'
|
|
|
|
const serverInfo =
|
|
|
|
`express/${require('express/package.json').version} ` +
|
|
|
|
`vue-server-renderer/${require('vue-server-renderer/package.json').version}`
|
|
|
|
|
|
|
|
const app = express()
|
|
|
|
|
|
|
|
function createRenderer(bundle, options) {
|
|
|
|
// https://github.com/vuejs/vue/blob/dev/packages/vue-server-renderer/README.md#why-use-bundlerenderer
|
|
|
|
return createBundleRenderer(bundle, Object.assign(options, {
|
|
|
|
// for component caching
|
|
|
|
cache: LRU({
|
|
|
|
max: 1000,
|
|
|
|
maxAge: 1000 * 60 * 15
|
|
|
|
}),
|
|
|
|
// this is only needed when vue-server-renderer is npm-linked
|
|
|
|
basedir: resolve('./dist'),
|
|
|
|
// recommended for performance
|
|
|
|
runInNewContext: false
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
let renderer
|
|
|
|
let readyPromise
|
|
|
|
const templatePath = resolve('./index.html')
|
|
|
|
if (isProd) {
|
|
|
|
// In production: create server renderer using template and built server bundle.
|
|
|
|
// The server bundle is generated by vue-ssr-webpack-plugin.
|
|
|
|
const template = fs.readFileSync(templatePath, 'utf-8')
|
|
|
|
const bundle = require('./dist/vue-ssr-server-bundle.json')
|
|
|
|
// The client manifests are optional, but it allows the renderer
|
|
|
|
// to automatically infer preload/prefetch links and directly add <script>
|
|
|
|
// tags for any async chunks used during render, avoiding waterfall requests.
|
|
|
|
const clientManifest = require('./dist/vue-ssr-client-manifest.json')
|
|
|
|
renderer = createRenderer(bundle, {
|
|
|
|
template,
|
|
|
|
clientManifest
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// In development: setup the dev server with watch and hot-reload,
|
|
|
|
// and create a new renderer on bundle / index template update.
|
|
|
|
readyPromise = require('./build/setup-dev-server')(
|
|
|
|
app,
|
|
|
|
templatePath,
|
|
|
|
(bundle, options) => {
|
|
|
|
renderer = createRenderer(bundle, options)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const serve = (path, cache) => express.static(resolve(path), {
|
|
|
|
maxAge: cache && isProd ? 1000 * 60 * 60 * 24 * 30 : 0
|
|
|
|
})
|
|
|
|
|
|
|
|
app.use(compression({ threshold: 0 }))
|
|
|
|
app.use('/static', serve('./dist/static', true))
|
|
|
|
|
|
|
|
// since this app has no user-specific content, every page is micro-cacheable.
|
|
|
|
// if your app involves user-specific content, you need to implement custom
|
|
|
|
// logic to determine whether a request is cacheable based on its url and
|
|
|
|
// headers.
|
|
|
|
// 1-second microcache.
|
|
|
|
// https://www.nginx.com/blog/benefits-of-microcaching-nginx/
|
|
|
|
app.use(microcache.cacheSeconds(1, req => useMicroCache && req.originalUrl))
|
|
|
|
|
|
|
|
function render(req, res) {
|
|
|
|
const s = Date.now()
|
|
|
|
|
|
|
|
res.setHeader("Content-Type", "text/html")
|
|
|
|
res.setHeader("Server", serverInfo)
|
|
|
|
|
|
|
|
const handleError = err => {
|
|
|
|
if (err.url) {
|
|
|
|
res.redirect(err.url)
|
|
|
|
} else if (err.code === 404) {
|
|
|
|
res.status(404).send('404 | Page Not Found')
|
|
|
|
} else {
|
|
|
|
// Render Error Page or Redirect
|
|
|
|
res.status(500).send('500 | Internal Server Error')
|
|
|
|
console.error(`error during render : ${req.url}`)
|
|
|
|
console.error(err.stack)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const context = {
|
|
|
|
title: 'Vue HN 2.0', // default title
|
|
|
|
url: req.url
|
|
|
|
}
|
|
|
|
renderer.renderToString(context, (err, html) => {
|
|
|
|
if (err) {
|
|
|
|
return handleError(err)
|
|
|
|
}
|
|
|
|
res.send(html)
|
|
|
|
if (!isProd) {
|
|
|
|
console.log(`whole request: ${Date.now() - s}ms`)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
app.get('*', isProd ? render : (req, res) => {
|
|
|
|
readyPromise.then(() => render(req, res))
|
|
|
|
})
|
|
|
|
|
|
|
|
const port = process.env.PORT || 8080
|
|
|
|
app.listen(port, () => {
|
|
|
|
console.log(`server started at localhost:${port}`)
|
2018-06-07 14:59:05 +08:00
|
|
|
})
|