解读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启动入口
  1. 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')
      )
    })
    
  2. 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文件中。

源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wintermelon__zzz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值