You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
查看官网build文件夹有个 setup-dev-server 文件 ,该文件主要用来本地开发时 ssr 的 hot-reload , 因此会看到 Server端的watch
constserverCompiler=webpack(serverConfig)constmfs=newMFS()serverCompiler.outputFileSystem=mfsserverCompiler.watch({},(err,stats)=>{if(err)throwerrstats=stats.toJson()if(stats.errors.length)return// read bundle generated by vue-ssr-webpack-pluginbundle=JSON.parse(readFile(mfs,'vue-ssr-server-bundle.json'))update()})
client 端的 hot middleware
clientConfig.entry.app=['webpack-hot-middleware/client',clientConfig.entry.app]clientConfig.output.filename='[name].js'clientConfig.plugins.push(newwebpack.HotModuleReplacementPlugin(),newwebpack.NoEmitOnErrorsPlugin())// dev middlewareconstclientCompiler=webpack(clientConfig)constdevMiddleware=require('webpack-dev-middleware')(clientCompiler,{publicPath: clientConfig.output.publicPath,noInfo: true})app.use(devMiddleware)clientCompiler.plugin('done',stats=>{stats=stats.toJson()stats.errors.forEach(err=>console.error(err))stats.warnings.forEach(err=>console.warn(err))if(stats.errors.length)returnclientManifest=JSON.parse(readFile(// 实时更新vue-ssr-client-manifest.jsondevMiddleware.fileSystem,'vue-ssr-client-manifest.json'))update()})// hot middlewareapp.use(require('webpack-hot-middleware')(clientCompiler,{heartbeat: 5000}))
以及 index.html 的热载入
// read template from disk and watchtemplate=fs.readFileSync(templatePath,'utf-8')chokidar.watch(templatePath).on('change',()=>{template=fs.readFileSync(templatePath,'utf-8')console.log('index.html template updated.')update()})
// 通过静态服务实时获取最新的 vue-ssr-client-manifest.jsonconstclientManifestResp=awaitaxios.get(`http://localhost:${process.env.STATIC_PORT}/vue-ssr-client-manifest.json`)constclientManifest=clientManifestResp.dataconstrenderer=createBundleRenderer(bundle,{runInNewContext: false,// index.html 直接读取文件内容template: fs.readFileSync(path.resolve(__dirname,'../public/index.ssr.html'),'utf-8'),
clientManifest
})consthtml=awaitrenderToString(ctx,renderer)ctx.body=html}// 静态资源我们借助静态服务转发请求asyncfunctionhandleStaticResourceRequest(ctx){console.log('get static resource: ',ctx.path)letresourcetry{resource=awaitaxios.get(`http://localhost:${process.env.STATIC_PORT}/${ctx.path}`,{responseType: 'stream'})}catch(e){console.log('static resources not found',ctx.path)ctx.status='404'return}ctx.body=resource.data}constrouter=newRouter()// because it can't read dist statice files on development mode, so we can proxy request by statice server.router.get(/^\/((js|css|img|)\/(.*)|favicon\.ico)$/,handleStaticResourceRequest)
背景
项目中需要用到vue-ssr, 看了一遍官网的例子 HackerNews Demo
, 发现还是手写webpack 进行搭建的。另外也发现比较老旧了,想着应该可以vue-cli 进行编译打包提速。那么在这期间遇到了一些问题,本文主要给大家讲述这搭建期间遇到的一些问题以及规避办法。
过程
运行官网例子HackerNews Demo
在下载官网例子后,本地运行时会发现页面是空白页面打不开。这是因为官网调用的接口服务器在国外需要翻墙。issue322。如果只是想要运行起来看看效果,可以把 源码 中的 asyncData 注释掉。
vue-cli 搭建ssr
初始化项目
先用vue-cli 生成csr 项目,这样dependencies,eslint, babel 等配置就已经构建好了。然后讲vue-ssr 所需要的entry-client、entry-server、store、router 等配置好。
然后将 index.html 中 入口节点 app去除,
增加ssr 出口
至此整个SSR 的源码部分已经搞定
构建
1.增加编译命令
官网是通过 指定不同webpack config 文件进行编译。我们因为统一用vue.config.js 进行编译,那么为了区分client / server 编译我们指定编译目标 WEBPACK_TARGET,如下:
由于可能跨平台执行脚本(如:打包过程是专门的流水线服务linux 环境执行),所以我们增加cross-env
2.编译入口
先区分端类型,指定入口文件
增加 vue-server-renderer plugin 配置
3.server 端配置
阅读官网build 文件夹内容,会看到一个client/server 共用的 base 文件。里面都是相关的 babel、loader、uglify 等配置,这些配置其实已经集成到了vue.config.js 中,我们不需要再关心了。(vue-cli 的好处提现出来了)
查看官网的ssr指南-构建配置, 看到如下配置:

我们将此转义到vue-config.js 中,如下:
这里需要重点注意的是 webpack-node-externals 依赖的版本号,从2.1.0 开始 已经不在支持 whitelist 配置了, 改为 allowlist
注意事项:
1)由于server 端渲染只需要一个entry file 所以对于 vue.config.js 默认配置的 optimization 是不需要的。因此我们增加如下配置:
2)vue.config.js 默认慧配置hot modules reload, 此配置在服务端是不需要的,因此我们删掉:
4.client 端配置
如官网ssr指南-构建配置, 所说:客户端配置 (client config) 和基本配置 (base config) 大体上相同。所以客户端不再增加配置
5.node 渲染服务
官网-Server 对于静态资源直接读取,剩余的路由走 server-render。我这里用koa 作为web框架,配置如下:

外层主文件
需要注意的是我们使用vue-config.js 中,静态资源会分布到js、css、img 等文件夹,我们部署到服务器直接通过[域名]/js/xxx.js这样去访问静态资源。所以增加了模式匹配 /^/(?!(js|css|img))// 。当然也可以明确路由如:
**注意不能用 router.get(' ', handleRequest) , 因为koa-router 7 (当前9.4)以后不允许使用 '' **
6.ssr development 模式
查看官网build文件夹有个 setup-dev-server 文件 ,该文件主要用来本地开发时 ssr 的 hot-reload , 因此会看到 Server端的watch
client 端的 hot middleware
以及 index.html 的热载入
那么 vue.config.js 中,我们可以借助其本身的静态服务(dev.server),来帮助我们热更新静态资源,同时vue-ssr-client-mainifest.json 的更新我们也可以交给静态服务来处理:
这里需要重点注意请求静态资源时,一定要配置 {responseType: 'stream'},不然对于图片资源会加载失败。
剩下的server 端热更新,由于我们用的vue.config.js ,我们需要找到其中的webpack config ,参考官网进行配置。

其中的 webpack config 在 vue-cli 官网中有说明,
那么剩下的就是配置了,如下:
至此我们的本地服务配置完毕,接下来我们添加script 命令
由于vue.config.js 默认配置dev server 端口号为8090,我们通过vue-cli-service serve 很容易与其他服务冲突,导致我们的ssr 渲染失败。所以这里我们配置了环境变量STATIC_PORT 为8090, 使得我们静态服务端口号为8090,防止与其他服务端口号冲突。同时我们在vue.config.js 中更改dev.server 端口
至此我们配置完成,运行 npm run serve 试试,启动正常。不幸的是页面访问时报错了,仔细看发现 ssr node 服务渲染时报 document not defined
7.解决 document not defined 问题
我们代码中并没有明确用 document.xxxx 那哪来的呢,找了一圈发现 是vue-cli 默认对于css 处理用了 mini-css-extract-plugin 而该插件在extract css 文件时会插入document.getElementsByTagName [参考] (vuejs/vue-router#2380 (comment))
知道问题根结我们要去除该插件,vue-cli 用@sodatea 给的解决方案。
本质是它自己写了个loader。然后将mini-css-extract-plugin 进行替换
这就好办了,我们偷懒将这个loader 复制到本地吧。同时配置vue-config.js
到此我们的整个项目已经完全可以run 了。
8.其他
1)增加个编译进度条
2)本地更文件后,服务页面不能自动刷新,需要手动更新。检查错误一看原来静态资源会有个websocket 服务,会获取最新的hot-update..js 以及 hot-update.json。那么我们继续用静态服务进行代理改文件,在上述的本地node服务中增加如下代码:
好了,大功告成了。
9. 降级方案CSR
我们用了vue.config.js 编译其实产物也可以支持CSR哦。如果业务有需要(ssr 失败降级为csr), 我们增加 csr 的 index.html,vue.config.js 自动会将其放到dist 目录中。
对于ssr 模板文件html 我们新增index.ssr.html,并在vue.config.js 中将我们的 index.ssr.html 复制到dist 目录就可以了。
最后
The text was updated successfully, but these errors were encountered: