gpt4 book ai didi

javascript - React Router v5 附带代码拆分和使用服务器端渲染的数据预取

转载 作者:行者123 更新时间:2023-12-03 17:09:03 26 4
gpt4 key购买 nike

我有一个项目使用 react 路由器 v3 仅出于一个原因。原因是需要使用数据预取进行服务器端渲染,最方便的方法是保留 集中路由配置objectarray并遍历匹配的元素以从服务器端的 API 获取数据。稍后的数据将与响应 HTML 一起传递给客户端,并存储在 JSON 格式字符串的变量中。
应用程序也使用代码拆分,但是使用 babel-plugin-transform-ensure-ignore在服务器端我可以直接获取组件而不是延迟加载和本地人 import方法将仅在客户端使用。
然而,上述结构不适用于 react 路由器 v5 ,因为它有点困难,因为我不能使用 @loadable/components ,如 react 路由器官方文件建议。根据我的观察 @loadable/components只是在服务器端生成 HTML,而不是给我实现 fetch 的组件负责服务器端逻辑的方法。
所以想请教一下的结构好webpack + react-router v5 + ssr + 数据预取 + redux + 代码拆分
我认为这很复杂,没有通用的解决方案,但是我可能错了。
任何方向或建议表示赞赏。

最佳答案

我从未尝试过@loadable/components ,但我用自定义的代码拆分实现做了类似的事情(SSR + 代码拆分 + 数据预取),我相信你应该改变你的数据预取方法。
如果我说得对,你的问题是你试图干预正常的 React 渲染过程,提前推断出你的渲染中将使用哪些组件,从而应该预取哪些数据。这种干预/推论不是 React API 的一部分,虽然我看到不同的人使用一些未记录的内部 React 东西来实现它,但从长远来看,它们都很脆弱,并且容易出现像你这样的问题。
我相信,一个更好的防弹方法是将 SSR 执行为几个正常的渲染 channel ,在每个 channel 中收集要预取的数据列表,获取它们,然后从一开始就使用更新重复渲染状态。我正在努力想出一个清晰的解释,但让我试试这样的例子。
比如说,一个组件 <A>您的应用程序树中的某处取决于异步获取的数据,这些数据应该存储在 some.path你的 Redux 商店。考虑一下:

  • 假设您从空的 Redux 存储开始,并且您还拥有 SSR 上下文(为此您可以重用 StaticRoutercontext ,或者使用 React 的 Context API 创建一个单独的)。
  • 您使用 ReactDOMServer.renderToString(..) 对整个应用程序执行非常基本的 SSR .
  • 当渲染器到达渲染组件时<A>在你的应用程序树的某个地方,无论它是否被代码分割,如果一切设置正确,该组件将可以访问 Redux 存储和 SSR 上下文。所以,如果 <A>看到当前渲染发生在服务器上,并且没有数据预取到 some.path Redux 商店的,<A>将“加载这些数据的请求”保存到 SSR 上下文中,并呈现一些占位符(或在没有预取这些数据的情况下呈现任何有意义的内容)。我所说的“加载这些数据的请求”是指 <A>实际上可以触发一个异步函数,该函数将获取数据,并将相应的数据 promise 推送到上下文中的专用数组。
  • 一次ReactDOMServer.renderToString(..)完成您将拥有:呈现的 HTML 标记的当前版本,以及收集在 SSR 上下文对象中的一组数据获取 promise 。您可以在此处执行以下操作之一:
  • 如果没有将 promise 收集到 SSR 上下文中,那么您呈现的 HTML 标记是最终的,您可以将它与 Redux 存储内容一起发送到客户端;
  • 如果有待处理的 Promise,但 SSR 已经花费了太长时间(从 (1) 开始计算),您仍然可以发送当前 HTML 和当前 Redux 存储内容,并且只依赖客户端获取任何丢失的数据,并完成渲染 (从而在服务器延迟和 SSR 完整性之间进行妥协)。
  • 如果可以等待,则等待所有未决的 promise ;将所有获取的数据添加到 Redux 存储的正确位置;重置 SSR 上下文;然后回到 (2),从头开始重复渲染,但更新了 Redux 存储内容。


  • 您应该看到,如果实现正确,它将与依赖异步数据的任意数量的不同组件一起工作,无论它们是否嵌套,以及您如何准确地实现代码拆分、路由等。重复渲染会产生一些开销通过,但我相信这是可以接受的。

    一个小代码示例,基于我使用的代码片段:
    SSR 循环( original code):
    const ssrContext = {
    // That's the initial content of "Global State". I use a custom library
    // to manage it with Context API; but similar stuff can be done with Redux.
    state: {},
    };

    let markup;
    const ssrStart = Date.now();
    for (let round = 0; round < options.maxSsrRounds; ++round) {
    // These resets are not in my original code, as they are done in my global
    // state management library.
    ssrContext.dirty = false;
    ssrContext.pending = [];

    markup = ReactDOM.renderToString((
    // With Redux, you'll have Redux store provider here.
    <GlobalStateProvider
    initialState={ssrContext.state}
    ssrContext={ssrContext}
    >
    <StaticRouter
    context={ssrContext}
    location={req.url}
    >
    <App />
    </StaticRouter>
    </GlobalStateProvider>
    ));

    if (!ssrContext.dirty) break;

    const timeout = options.ssrTimeout + ssrStart - Date.now();
    const ok = timeout > 0 && await Promise.race([
    Promise.allSettled(ssrContext.pending),
    time.timer(timeout).then(() => false),
    ]);
    if (!ok) break;

    // Here you should take data resolved by "ssrContext.pending" promises,
    // and place it into the correct paths of "ssrContext.state", before going
    // to the next SSR iteration. In my case, my global state management library
    // takes care of it, so I don't have to do it explicitly here.
    }
    // Here "ssrContext.state" should contain the Redux store content to send to
    // the client side, and "markup" is the corresponding rendered HTML.
    依赖异步数据的组件内部的逻辑将有点像这样:
    function Component() {
    // Try to get necessary async from Redux store.
    const data = useSelector(..);

    // react-router does not provide a hook for accessing the context,
    // and in my case I am getting it via my <GlobalStateProvider>, but
    // one way or another it should not be a problem to get it.
    const ssrContext = useSsrContext();

    // No necessary data in Redux store.
    if (!data) {
    // We are at server.
    if (ssrContext) {
    ssrContext.dirty = true;
    ssrContext.pending.push(
    // A promise which resolves to the data we need here.
    );

    // We are at client-side.
    } else {
    // Dispatch an action to load data into Redux store,
    // as appropriate for your setup.
    }
    }

    return data ? (
    // Return the complete component render, which requires "data"
    // for rendering.
    ) : (
    // Return an appropriate placeholder (e.g. a "loading" indicator).
    );
    }

    关于javascript - React Router v5 附带代码拆分和使用服务器端渲染的数据预取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67352004/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com