Webpack 4 和 react loadable 似乎没有为服务器端渲染创建正确的块

Posted

技术标签:

【中文标题】Webpack 4 和 react loadable 似乎没有为服务器端渲染创建正确的块【英文标题】:Webpack 4 and react loadable does not seems to create correct chunk for server side rendering 【发布时间】:2019-02-11 22:23:51 【问题描述】:

我正在尝试使用延迟加载导入创建一个 s-s-r react 应用程序。 一切正常,除了它没有获取所有需要的块

我也想知道这是否与基于服务器响应的动态组件有关

编辑 - 它实际上渲染了所有需要的块,但是当客户端接管并再次渲染时它会清除整个东西

由于它会重新渲染所有内容,因此速度会慢很多。

解析器发生在服务器端,当客户端接管时,它会获取更多 server.js

 const history = createHistory(
    initialEntries: [urlPath],
  )
  // const history = createHistory()
  const store = configureStore(history, 
    location: 
        ...
    ,

  )

  const context = 
  const htmlRoot = (
    <Provider store=store>
      <StaticRouter location=urlPath context=context>
        <AppRoot />
      </StaticRouter>
    </Provider>
  )

  // pre fetching data from api
  store
    .runSaga(rootSaga)
    .done.then(() => 

        const RTS = renderToString(htmlRoot) + printDrainHydrateMarks()
        const head = Helmet.renderStatic() 
        console.log(printDrainHydrateMarks())

        res.status(code).send(renderDom(RTS, port, host, storeState, head))
      
    )
    .catch(e => 
      console.log(e.message)
      res.status(500).send(e.message)
    )

  renderToString(htmlRoot)
  console.log(printDrainHydrateMarks())

  store.close()
 else 
  res.status(500).send(_err)

产品服务器

Loadable.preloadAll().then(() => 
  app.listen(PROD_PORT, (error) => 

  )
);

客户端

Loadable.preloadReady().then(() => 
    hydrate(
      <Provider store=store>
        <ConnectedRouter history=history>
          <AppRoot />
        </ConnectedRouter>
      </Provider>,
      domRoot,
    )
  )

分块设置

    styles: 
      name: 'styles',
      test: /\.css$/,
      chunks: 'all',
      enforce: true
    ,

欢迎任何意见或建议

有人建议尝试使用 window.onload = () => 但这种方法似乎也很慢。

【问题讨论】:

好问题。但我仍然没有任何想法。 【参考方案1】:

您应该使用 ReactLoadablePlugin 来获得可加载的导入列表:

new ReactLoadablePlugin(
  filename: './build/react-loadable.json',
)

使用“react loadable capture”,您可以找出渲染时需要哪些动态组件,您可以将它们的捆绑包添加到您的头文件中:

const content = ReactDOMServer.renderToString(
  <Loadable.Capture report=moduleName => modules.push(moduleName)>
    <Provider store=configureStore()>
      <StaticRouter location=req.url context=context>
        <App />
      </StaticRouter>

    </Provider>
  </Loadable.Capture>
);

  let bundles = getBundles(stats, modules);
  bundles.map((item) => 
      //add js to header
      )

这可以防止 react loadable 清除内容并重新渲染它。

要配置 Webpack 以根据动态加载的组件输出正确的块,请执行以下操作:

 optimization: 
    splitChunks: 
      cacheGroups: 
          default: false,
          vendors: false,
      
  

此配置在 Webpack v4 中适用于我。

您可以在此处找到使用 React 可加载服务器端渲染的完整文档:

https://github.com/jamiebuilds/react-loadable

【讨论】:

【参考方案2】:

我正在使用react-universal-component 和flush chuck 的以路由为中心的代码拆分。对不起,长代码 sn-p/伪代码,我已尽力使其更短。 :)

先建一个数组,用于react router映射对应的组件

let routeConfig = [
  path: '/foo', component: 'Foo',
  path: '/bar', component: 'Bar'
];

universal 函数确保组件及其子组件可以在服务器端和客户端正确导入。相应地延迟加载拆分代码是足够聪明的。

import universal from 'react-universal-component';
const routes = routeConfig.map((item) =>
  let route = ;
  route.path = item.path;
  route.component = universal(import(`./container/$item.component`), options);
  return route;
);

在 React Component 中渲染路由器。

class App extends React.Component 
 render() 
    return (
      <div>
        <Switch>
          routes.map( route => <Route key= route.path   ...route  />)
        </Switch>
      </div>
);

配置 webpack 定义代码拆分名称。

  output: 
    filename: '[name].js',
    chunkFilename: '[name].js',
    path: path.resolve(__dirname, '../dist/client'),
    publicPath: '/xxxxxx/'
  ,
  plugins: [
    new MiniCssExtractPlugin(
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: '[name].css',
      chunkFilename: '[id].css'
  )],
  optimization: 
    splitChunks: 
      chunks: 'initial',
      cacheGroups: 
        vendors: 
          test: /[\\/]node_modules[\\/]/, 
          name: 'vendor' // bundle all the npm module as vendor.js

最后,server.js 将使用flush chuck 进行代码拆分。

const chunkNames = flushChunkNames();
const js, styles, cssHash, scripts, stylesheets = flushChunks(clientStats, chunkNames);
res.send(`<!doctype html>
  <html>
    <head>
      $styles
    </head>
    <body>
      <div id="root">$app</div>
      $cssHash
      $js
    </body>
  </html>`);

【讨论】:

【参考方案3】:

你应该考虑使用 ReactLoadables-s-rAddon,它比 ReactLoadable see this link for further info 提供的插件效果更好。就我而言,它有很大的不同!

【讨论】:

【参考方案4】:

如果您正在使用 ts-loader 尝试设置:

tsconfig.json


  .
  .
  .

  "module" : "esnext",
  "moduleResolution": "Node",
  .
  .
  .

【讨论】:

以上是关于Webpack 4 和 react loadable 似乎没有为服务器端渲染创建正确的块的主要内容,如果未能解决你的问题,请参考以下文章

react-loadable原理浅析

React_loadable

React-Router4 按需加载的4种实现

使用分块对 React s-s-r 应用程序进行 A/B 测试

[react] 你有使用过loadable组件吗?它帮我们解决了什么问题?

从多个类导出的 js 中进行 react-loadable 导入