详解Vue SSR( Vue2 + Koa2 + Webpack4)配置指南(2)

'use strict' /** * From koa-static */ const { resolve } = require('path') const assert = require('assert') const send = require('koa-send') /** * Expose `serve()`. */ module.exports = serve /** * Serve static files from `root`. * * @param {String} root * @param {Object} [opts] * @return {Function} * @api public */ function serve (root, opts) { opts = Object.assign({}, opts) assert(root, 'root directory is required to serve files') // options opts.root = resolve(root) if (opts.index !== false) opts.index = opts.index || 'index.html' if (!opts.defer) { return async function serve (ctx, next) { let done = false if (ctx.method === 'HEAD' || ctx.method === 'GET') { if (ctx.path === 'https://www.jb51.net/' || ctx.path === '/index.html') { // exclude index.html file await next() return } try { done = await send(ctx, ctx.path, opts) } catch (err) { if (err.status !== 404) { throw err } } } if (!done) { await next() } } } return async function serve (ctx, next) { await next() if (ctx.method !== 'HEAD' && ctx.method !== 'GET') return // response is already handled if (ctx.body != null || ctx.status !== 404) return // eslint-disable-line try { await send(ctx, ctx.path, opts) } catch (err) { if (err.status !== 404) { throw err } } } }

我们可以看到, koa-static 仅仅是对 koa-send 进行了简单封装( yarn add koa-send )。接下来就是重头戏SSR相关的配置了, config/ssr.js :

const fs = require('fs') const path = require('path') const chalk = require('chalk') const LRU = require('lru-cache') const { createBundleRenderer } = require('vue-server-renderer') const isProd = process.env.NODE_ENV === 'production' const setUpDevServer = require('./setup-dev-server') const HtmlMinifier = require('html-minifier').minify const pathResolve = file => path.resolve(__dirname, file) module.exports = app => { return new Promise((resolve, reject) => { const createRenderer = (bundle, options) => { return createBundleRenderer(bundle, Object.assign(options, { cache: LRU({ max: 1000, maxAge: 1000 * 60 * 15 }), basedir: pathResolve('../dist/web'), runInNewContext: false })) } let renderer = null if (isProd) { // prod mode const template = HtmlMinifier(fs.readFileSync(pathResolve('../public/index.html'), 'utf-8'), { collapseWhitespace: true, removeAttributeQuotes: true, removeComments: false }) const bundle = require(pathResolve('../dist/web/vue-ssr-server-bundle.json')) const clientManifest = require(pathResolve('../dist/web/vue-ssr-client-manifest.json')) renderer = createRenderer(bundle, { template, clientManifest }) } else { // dev mode setUpDevServer(app, (bundle, options, apiMain, apiOutDir) => { try { const API = eval(apiMain).default // eslint-disable-line const server = API(app) renderer = createRenderer(bundle, options) resolve(server) } catch (e) { console.log(chalk.red('\nServer error'), e) } }) } app.use(async (ctx, next) => { if (!renderer) { ctx.type = 'html' ctx.body = 'waiting for compilation... refresh in a moment.' next() return } let status = 200 let html = null const context = { url: ctx.url, title: 'OK' } if (/^\/api/.test(ctx.url)) { // 如果请求以/api开头,则进入api部分进行处理。 next() return } try { status = 200 html = await renderer.renderToString(context) } catch (e) { if (e.message === '404') { status = 404 html = '404 | Not Found' } else { status = 500 console.log(chalk.red('\nError: '), e.message) html = '500 | Internal Server Error' } } ctx.type = 'html' ctx.status = status || ctx.status ctx.body = html next() }) if (isProd) { const API = require('../dist/api/api').default const server = API(app) resolve(server) } }) }

这里新加入了 html-minifier 模块来压缩生产环境的 index.html 文件( yarn add html-minifier )。其余配置和官方给出的差不多,不再赘述。只不过Promise返回的是 require('http').createServer(app.callback()) (详见源码)。这样做的目的是为了共用一个koa2实例。此外,这里拦截了 /api 开头的请求,将请求交由API Server进行处理(因在同一个Koa2实例,这里直接next()了)。在 public 目录下必须存在 index.html 文件:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/9bc17fb47e553302b2ab3e4e98f6fcb3.html