上一节讨论了VueSSR的构建流程,构建出来的clientManifest和serverBundle最终会被转换成html,这一节咱们深刻vue-server-renderer的核心内容,看看它们都通过了哪些的处理。这一节的内容包括:javascript
阅读源码先总体查看写下代码文件结构和入口文件暴露的接口,而后运行一段demo断点来跟踪代码处理数据的细节,下面将以这段demo做为阅读代码的入口:css
vue-server-renderer
提供了两个API,createRenderer和createBundleRender。它们的用法同样,若是你阅读了源码你会知道createBundleRenderer实际上是在createRenderer上作了扩展,以提供官网上所说的下面几种特性:html
源码看到最后,我发现了一段能够能够大体的理解它的脉络的代码:vue
// templateRenderer.render方法内
if (this.inject) {
return (
template.head(context) +
(context.head || '') +
this.renderResourceHints(context) +
this.renderStyles(context) +
template.neck(context) +
content +
this.renderState(context) +
this.renderScripts(context) +
template.tail(context)
)
} else {
return (
template.head(context) +
template.neck(context) +
content +
template.tail(context)
)
}
复制代码
转换成图来展现:java
原来bundleRenderer以字符串拼接的方式将html的片断组合成整个html文档,整个html文档会划分为几个部分,这里只列出主要的部分,分别是头部、资源预加载和预取资源、inlineStyle、正文、state、script。这几个主要字符片断跟辅助的片断结合就结合成了输出的服务端渲染的html内容。webpack
前面咱们已经知道clientManifest的做用是记录文档资源文件的信息,bundleRenderer利用clientManifest的信息,推理出须要预加载和预取的资源和首屏加载的js资源文件,而后拼接成资源预加载和预取片断和script片断。而server bundle中记录中编译后的源代码,这正是html文档中正文的内容来源。web
那么server bundle是如何被处理成正文的呢?我将bundle的处理过程当中的关键步骤画成了流程图,以下: 数组
当执行createBundleRunner()时,在内部会执行compileModule(),生成一个处理编译后源码的函数evaluate。evaluate函数会将编译后文件源码包装成module对象,而后返回module.exports.defualt,它就是封装了文件源码的函数,执行这个函数就就至关于执行文件源码。当这个文件是入口文件时,返回的就是entry入口文件源码的封装函数,也就是runner,那么执行runner(context)至关于执行entry-server.js导出的函数,以下。服务器
export default context => {
return new Promise((resolve, reject) => {
const { app, router, store } = createApp(context.url)
...
resolve(app)
...
})
}
复制代码
app就是执行该函数后,Promise状态为fulfilled时往下传递的单页应用的vue根实例。以后app会传入renderToString方法,该方法内会调用render函数,递归根实例中的每一个子组件对象,渲染每一个子组件的template而后组装template,最终生成html中的正文片断content。app
render函数内实际上是执行了vue内部的render函数,执行组件的生命钩子函数,生成虚拟dom节点,只不过最后转化成了template字符串返回。最后templateRenderer.render方法将正文和其余文档片断组件成整个html文档返回。
bundle Renderer如何推断出预加载和预取资源的呢?
咱们如今已经知道html是在templateRenderer.render方法中组合的,在它里面有这么一句this.renderResourceHints(context)
,预加载和预取片断就是由它来生成的。如下是renderResourceHints方法的流程图:
从图中咱们很清晰得知道renderPreloadLinks方法和renderPrefetchLinks方法都调用了getUsedAsyncFiles方法,来拿到组件实例中所依赖到的代码chunk文件列表usedAsyncFiles,它是经过context._registeredComponents获得组件实例依赖的moduleIds,再根据clientManifest中的modules对象(记录modules之间的依赖关系)和all对象(记录着全部编译后的文件),找出moduleIds所对应的文件,这些文件就是初始渲染组件所依赖到的代码 chunk文件,也就是usedAsyncFiles。
usedAsyncFiles与preloadFiles(clientManifest中的initial文件数组)合并就是须要预加载的资源列表,usedAsyncFiles与prefetchFiles(clientManifest中的async文件数组)的差集就是预取的资源列表。