vue-server-renderer 无法构建捆绑文件
Posted
技术标签:
【中文标题】vue-server-renderer 无法构建捆绑文件【英文标题】:vue-server-renderer not able to build bundle file 【发布时间】:2019-01-12 07:28:29 【问题描述】:我正在尝试创建一个@vue/cli3 s-s-r 项目,但似乎无法生成服务器捆绑文件。客户端构建良好。
package.json 脚本
"scripts":
"serve": "npm run build && node scripts/serve",
"build": "npm run build:server && mv dist/vue-s-s-r-server-bundle.json bundle && npm run build:client && mv bundle dist/vue-s-s-r-server-bundle.json",
"build:client": "vue-cli-service build",
"build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build"
运行yarn serve
时出错
$ yarn serve
yarn run v1.9.2
$ npm run build && node scripts/serve
> project-name@0.1.0 build /path/to/file/project-name
> npm run build:server && mv dist/vue-s-s-r-server-bundle.json bundle && npm run build:client && mv bundle dist/vue-s-s-r-server-bundle.json
> project-name@0.1.0 build:server /path/to/file/project-name
> cross-env WEBPACK_TARGET=node vue-cli-service build
⠙ Building for production...(node:24514) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
⠏ Building for production...(node:24514) UnhandledPromiseRejectionWarning: Error: Server-side bundle should have one single entry file. Avoid using CommonsChunkPlugin in the server config.
at /path/to/file/project-name/node_modules/vue-server-renderer/server-plugin.js:58:13
at _err2 (eval at create (/path/to/file/project-name/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:22:1)
at callback (/path/to/file/project-name/node_modules/copy-webpack-plugin/dist/index.js:77:17)
at /path/to/file/project-name/node_modules/copy-webpack-plugin/dist/index.js:118:24
at <anonymous>
(node:24514) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:24514) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
vue.config.js
const path = require('path')
const Vues-s-rServerPlugin = require('vue-server-renderer/server-plugin')
const Vues-s-rClientPlugin = require('vue-server-renderer/client-plugin')
const nodeExternals = require('webpack-node-externals')
const merge = require('lodash.merge')
const TARGET_NODE = process.env.WEBPACK_TARGET === 'node'
const resolve = (file) => path.resolve(__dirname, file)
const target = TARGET_NODE
? 'server'
: 'client'
module.exports =
configureWebpack: () => (
entry:
app: `./src/entry-$target`
,
target: TARGET_NODE ? 'node' : 'web',
node: TARGET_NODE ? undefined : false,
plugins: [
TARGET_NODE
? new Vues-s-rServerPlugin()
: new Vues-s-rClientPlugin()
],
externals: TARGET_NODE ? nodeExternals(
whitelist: /\.css$/
) : undefined,
output:
libraryTarget: TARGET_NODE
? 'commonjs2'
: undefined
,
optimization:
splitChunks: undefined
,
resolve:
alias:
'@': resolve('src'),
'public': resolve('public')
),
chainWebpack: config =>
config.module
.rule('vue')
.use('vue-loader')
.tap(options =>
merge(options,
optimizes-s-r: false
)
)
最后是 server.js 文件:
/* eslint-disable no-console */
const fs = require('fs')
const path = require('path')
const express = require('express')
const compression = require('compression')
const favicon = require('serve-favicon')
const microcache = require('route-cache')
const Ouch = require('ouch')
var proxy = require('http-proxy-middleware')
const createBundleRenderer = require('vue-server-renderer')
const resolve = file => path.resolve(__dirname, file)
const devServerBaseURL = process.env.DEV_SERVER_BASE_URL || 'http://localhost'
const devServerPort = process.env.DEV_SERVER_PORT || 8080
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)
return createBundleRenderer(bundle, Object.assign(options,
runInNewContext: false
))
let renderer
const templatePath = path.resolve(__dirname, './src/index.template.html')
const bundle = require('./dist/vue-s-s-r-server-bundle.json')
const template = fs.readFileSync(templatePath, 'utf-8')
const clientManifest = require('./dist/vue-s-s-r-client-manifest.json')
renderer = createRenderer(bundle,
template,
clientManifest
)
if (process.env.NODE_ENV !== 'production')
app.use('/js/main*', proxy(
target: `$devServerBaseURL/$devServerPort`,
changeOrigin: true,
pathRewrite: function (path)
return path.includes('main')
? '/main.js'
: path
,
prependPath: false
))
app.use('/*hot-update*', proxy(
target: `$devServerBaseURL/$devServerPort`,
changeOrigin: true
))
app.use('/sockjs-node', proxy(
target: `$devServerBaseURL/$devServerPort`,
changeOrigin: true,
ws: true
))
const serve = (path, cache) => express.static(resolve(path),
maxAge: cache && isProd ? 1000 * 60 * 60 * 24 * 30 : 0
)
app.use('/js', express.static(path.resolve(__dirname, './dist/js')))
app.use('/css', express.static(path.resolve(__dirname, './dist/css')))
app.use(express.json())
app.use(compression( threshold: 0 ))
app.use(favicon('./public/favicon.ico'))
app.use('/public/manifest.json', serve('./manifest.json', true))
app.use('/public', serve('./public', true))
app.use('/public/robots.txt', serve('./robots.txt'))
app.get('/sitemap.xml', (req, res) =>
res.setHeader('Content-Type', 'text/xml')
res.sendFile(resolve('./public/sitemap.xml'))
)
// 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.
// 10-minute microcache.
// https://www.nginx.com/blog/benefits-of-microcaching-nginx/
const cacheMiddleware = microcache.cacheSeconds(10 * 60, req => useMicroCache && req.originalUrl)
const ouchInstance = (new Ouch()).pushHandler(new Ouch.handlers.PrettyPageHandler('orange', null, 'sublime'))
function render (req, res)
const start = Date.now()
res.setHeader('Content-Type', 'text/html')
res.setHeader('Server', serverInfo)
const context =
url: req.url,
res
const handleError = err =>
if (err.url)
res.redirect(err.url)
else if (err.code === 404)
res.status(404).send('404 | Page Not Found')
else
ouchInstance.handleException(err, req, res, output =>
console.log(JSON.stringify(err))
console.log('Error handled!')
)
renderer.renderToString(context, (err, html) =>
if (err) return handleError(err)
res.end(html)
!isProd && console.log(`whole request: $Date.now() - startms`)
)
app.get('*', render, cacheMiddleware)
const port = process.env.PORT || 8095
const host = process.env.HOST || '0.0.0.0'
app.listen(port, host, () =>
console.log(`server started at $host:$port`)
)
这个错误似乎来自 vue-server-renderer,并且与 https://github.com/vuejs/vue/issues/5553 有某种关系,但它不需要提供任何关于如何解决这个问题的线索。所有的 Promise 都有 catch 块,所以不应该处理任何拒绝。这只发生在 WEBPACK_TARGET === 'node'
并因此使用 Vues-s-rServerPlugin 时。
【问题讨论】:
【参考方案1】:我是这样解决的:
const base = require("@vue/cli-service/webpack.config");
delete base.optimization;
module.exports = merge(base,
... config related to s-s-r ...
);
【讨论】:
以上是关于vue-server-renderer 无法构建捆绑文件的主要内容,如果未能解决你的问题,请参考以下文章