server.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. const fs = require('fs')
  2. const path = require('path')
  3. const LRU = require('lru-cache')
  4. const express = require('express')
  5. const favicon = require('serve-favicon')
  6. const compression = require('compression')
  7. const microcache = require('route-cache')
  8. const resolve = file => path.resolve(__dirname, file)
  9. const { createBundleRenderer } = require('vue-server-renderer')
  10. const isProd = process.env.NODE_ENV === 'production'
  11. const useMicroCache = process.env.MICRO_CACHE !== 'false'
  12. const serverInfo =
  13. `express/${require('express/package.json').version} ` +
  14. `vue-server-renderer/${require('vue-server-renderer/package.json').version}`
  15. const app = express()
  16. function createRenderer (bundle, options) {
  17. return createBundleRenderer(bundle, Object.assign(options, {
  18. cache: LRU({
  19. max: 1000,
  20. maxAge: 1000 * 60 * 15
  21. }),
  22. basedir: resolve('./dist'),
  23. // 此配置项 优化性能
  24. runInNewContext: false
  25. }))
  26. }
  27. let renderer
  28. let readyPromise
  29. const templatePath = resolve('./src/index.html')
  30. if(isProd) {
  31. const template = fs.readFileSync(templatePath, 'utf-8')
  32. const bundle = require('./dist/vue-ssr-server-bundle.json')
  33. const clientManifest = require('./dist/vue-ssr-client-manifest.json')
  34. renderer = createRenderer(bundle, {
  35. template,
  36. clientManifest
  37. })
  38. } else {
  39. readyPromise = require('./build/setup-dev-server')(
  40. app,
  41. templatePath,
  42. (bundle, options) => {
  43. renderer = createRenderer(bundle, options)
  44. }
  45. )
  46. }
  47. const serve = (path, cache) => express.static(resolve(path), {
  48. maxAge: cache && isProd ? 1000 * 60 * 60 * 24 * 30 : 0
  49. })
  50. app.use(compression({ threshold: 0}))
  51. app.use(favicon('./public/favicon.ico'))
  52. app.use('/dist', serve('./dist', true))
  53. app.use('/public', serve('./public', true))
  54. app.use('/manifest.json', serve('./manifest.json', true))
  55. app.use('/service-worker.js', serve('./dist/service-worker.js'))
  56. app.use(microcache.cacheSeconds(1, req => useMicroCache && req.originalUrl))
  57. function render (req, res) {
  58. const s = Date.now()
  59. res.setHeader("Content-type", "text/html")
  60. res.setHeader("Server", serverInfo)
  61. const handleError = err => {
  62. if(err.url) {
  63. res.redirect(err.url)
  64. } else if(err.code === 404) {
  65. res.status(404).send('404 | Page Not Found')
  66. } else {
  67. res.status(500).send('500 | Internal Server Error')
  68. console.error(`error during render : ${req.url}`)
  69. console.error(err.stack)
  70. }
  71. }
  72. const context = {
  73. title : '微剧院 - 作者后台',
  74. url : req.url
  75. }
  76. renderer.renderToString(context, (err, html) => {
  77. if(err) {
  78. return handleError(err)
  79. }
  80. res.send(html)
  81. if(!isProd) {
  82. console.log(`whole request: ${Date.now() - s}ms`)
  83. }
  84. })
  85. }
  86. app.get('*', isProd ? render : (req, res) => {
  87. readyPromise.then(() => render(req, res))
  88. })
  89. const port = process.env.PORT || 1024
  90. app.listen(port, () => {
  91. console.log(`server started at localhost:${port}`)
  92. })