text: const fs = require('fs')
  const path = require('path')
  const os = require('os')
  const opn = require('opn')
  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('./src/index.template.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(favicon('./src/static/images/logo-48.png'))
  app.use('/dist', serve('./dist', true))
  app.use('/public', serve('./public', true))
  app.use('/manifest.json', serve('./manifest.json', true))
  app.use('/service-worker.js', serve('./dist/service-worker.js'))
  
  app.all('*', function(req, res, next) {
      res.header('Access-Control-Allow-Origin', '*')
      res.header('Access-Control-Allow-Headers', 'Content-Type,Content-Length, Authorization, Accept,X-Requested-With')
      res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
      // res.header('X-Powered-By', ' 3.2.1')
      if (req.method == 'OPTIONS') res.send(200) /*让options请求快速返回*/
      else next()
  })
  
  // 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
      }
      //1:renderer.renderToString
      // renderer.renderToString(context, (err, html) => {
      //     if (err) {
      //         return handleError(err)
      //     }
      //     res.send(html)
      //     if (!isProd) {
      //         console.log(`whole request: ${Date.now() - s}ms`)
      //     }
      // })
      //2:流式渲染 renderer.renderToStream
      const stream=renderer.renderToStream(context);
      let  html=''
      stream.on('data', data => {
          html += data.toString()
      })
      stream.on('end', () => {
          res.send(html)// 渲染完成
          if (!isProd) {
              console.log(`whole request: ${Date.now() - s}ms`)
          }
      })
      stream.on('error', err => {
          handleError(err)
      })
  
  }
  
  app.get('*', isProd ? render : (req, res) => {
      readyPromise.then(() => render(req, res))
  })
  function getAddress() {
      let iptable = '',
          ifaces = os.networkInterfaces()
      for (let dev in ifaces) {
          ifaces[dev].forEach(function(details, alias) {
              if (details.family === 'IPv4') {
                  iptable = details.address
              }
          })
      }
      return iptable
      // return '192.168.183.25'
  }
  
  const port = process.env.PORT || 8080
  const server=app.listen(port, getAddress(),() => {
      console.log(`server started at ${server.address().address}:${server.address().port}`)
      let url = `http://${server.address().address}:${server.address().port}`
      //opn(url)
  })
  