解读vue-server-renderer源码并在react中的实现

​ 在博客开发的过程中,有这样一个需求想解决,就是在SSR开发环境中,服务端的代码是是直接通过webpack打包成文件(因为里面包含同构的代码,就是服务端与客户端共享前端的组件代码),写到磁盘里,然后在启动打包好的入口文件来启动服务。但是我不想在开发环境把文件打包到磁盘中,想直接打包在内存中,这样不仅能优化速度,还不会因开发环境产生多余文件。还有就是webpack对require的处理,会导致路径映射的问题,包括对require变量的问题。所以我就想只有组件相关的代码进行webpack编译,别的无关的服务端代码不进行webpack编译处理。

但是这中间有个问题一直悬而不决,就是如何引入内存中的文件。包括在引入这个文件后,如何把关联的文件一起引入,如通过require(module)引入的模块,于是我想到以前在给vue做ssr的时候用到的vue-server-renderer这个库,这个是没有直接打出文件,而是把文件打入了内存中。但是他却能获取到文件,并执行文件获取到结果。于是就开启了这次的研究之旅。

实现

先讲下项目这块的实现流程,然后在讲下vue-server-renderer这个包是如何解决这个问题的,以此在react中的实现。

|-- webpack | |-- webpack.client.js // entry => clilent-main.js | |-- webpack.server.js // entry => server-main.js |-- client // 客户端代码 | |-- app.js | |-- client-main.js // 客户端打包入口 | |-- server-main.js // server端打包代码入口 |-- server // server端代码 | |-- ssr.js // ssr启动入口

client-main.js, 客户端打包一份代码,就是正常的打包, 打包出对应的文件。

import React, { useEffect, useState } from 'react' import ReactDom from 'react-dom' import App from './app' loadableReady(() => { ReactDom.hydrate( <Provider store={store}> <App /> </Provider>, document.getElementById('app') ) })

server-main.js,因为是SSR,所以在服务端也需要打包一份对应的js文件,用于ssr渲染。我这里是打算在这块直接处理完组件相关的数据,返回html,到时候服务端直接引入这个文件,获取html返回给前端就行。这是我的项目的处理,vue官方demo会有点区别,他是直接返回的app实例(new Vue(...), 然后在vue-server-renderer库中解析这个实例,最后同样也是返回解析好的html字符串。这里会有点区别,原理还是一样。

// 返回一个函数,这样可以传入一些参数,用来传入服务端的一些数据 import { renderToString } from 'react-dom/server' export default async (context: IContext, options: RendererOptions = {}) => { // 获取组件数据 ... // 获取当前url对应的组件dom信息 const appHtml = renderToString( extractor.collectChunks( <Provider store={store}> <StaticRouter location={context.url} context={context as any}> <HelmetProvider context={helmetContext}> <App /> </HelmetProvider> </StaticRouter> </Provider> ) ) // 渲染模板 const html = renderToString( <HTML>{appHtml}</HTML> ) context.store = store return html }

3. `ssr.js`, 因为这些文件我都是打在内存中的。所以我需要解析内存中的文件,来获取`server-main.js`中的函数,执行他,返回html给前端。 ```typescript // start方法是执行webpack的node端代码,用于把编译的文件打入内存中。 import { start } from '@root/scripts/setup' // 执行他,createBundleRenderer方法就是用来解析在server端打包的代码 start(app, ({ loadableStats, serverManifest, inputFileSystem }) => { renderer = createBundleRenderer({ loadableStats, serverManifest, inputFileSystem }) }) // 执行server-main.js中的函数并获取html const html = await renderer.renderToString(context) ctx.body = html

客户端的好说,通过创建html模板,然后把当前路由对应的资源(js, css,..)引入,访问的时候,浏览器直接拉取资源就行(这块是通过@loadable/webpack-plugin、@loadable/server、@loadable/component来进行资源的加载与获取,此处不做过多介绍,此文重点不在这个)。
这块的重点就是如何在内存中解析server-main.js这个被打包出来的需要在服务端引用的代码。

我们来看vue ssr的官方代码: vue-hackernews-2.0

const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') module.exports = merge(base, { target: 'node', devtool: '#source-map', entry: './src/server-main.js', output: { filename: 'server-bundle.js', libraryTarget: 'commonjs2' }, plugins: [ new VueSSRServerPlugin() ] })

上面用到了一个vue-server-renderer/server-plugin, 这个插件的主要功能是干嘛呢,其实就是对webpack中的资源做了下处理,把其中的js资源全部打在了一个json文件中。

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

转载注明出处:https://www.heiqu.com/zygfyf.html