React: 【雨伞】释放悬念

创建于 2018-07-13  ·  83评论  ·  资料来源: facebook/react

让我们用这个 issue 来跟踪将 Suspense 发布到开源的剩余任务。

初始版本 (MVP)

  • [x] 从任何渲染阶段函数中读取上下文的 API (@acdlite) [#13139]
  • [x] 隐藏超时内容而不是删除它 (@acdlite) [#13120]
  • [] 每个 React 根目录自动注入上下文提供程序 (@acdlite) [#13293]
  • [ ] 从AsyncMode删除unstable_前缀(也许?)
  • [ ] 支持同步 thenables,以及在渲染阶段完成之前解决的承诺。

    • [ ] 确认正确处理了引发错误的同步 thenable

  • [ ] 确认它适用于<div hidden> [#13089]
  • [ ] 为什么即使我在点击下一个之前等待每个细节链接的时间都少于占位符延迟(参见推文),为什么一个接一个点击灯具中的几个细节链接最终会导致一个大占位符?

简单缓存提供程序

  • [ ] 缓存失效 (@acdlite) [#13337]
  • [ ] 订阅 (@acdlite) [#13337]
  • [ ] 决定真实姓名

代码拆分

  • [x] 支持 promise 作为组件类型
  • [x](也许)开源lazyLoadComponent

测试渲染器

  • [ ] 完成flushAllyield等的公共 API

    • 暂定计划是为每个主要测试框架发布自定义匹配器,la #13236。

文档

  • [ ] 博文
  • [ ] React.Placeholder
  • [] 简单缓存提供者
  • [ ] 未命名的代码拆分库

跟进

软过期(https://github.com/facebook/react/issues/14248)

  • [] 为非祖先的就地加载指标实现 API
  • [ ] 如果速度足够快,请确保有一种方法可以避免闪烁内联微调器

流媒体服务器渲染器

  • [] 实现一个流媒体服务器渲染器,就像@acdlite的 ZEIT 演讲中的那个一样
  • [ ] 部分水合作用

相关:时间切片伞(https://github.com/facebook/react/issues/13306)

React Core Team Umbrella

最有用的评论

@mschipperheyn

这是一个多年的项目。 诚实的回答是,它产生的工作比我们两年前开始时想象的要多。

但好消息是,因为我们现在在生产中大量使用它,缺失的部分很清楚,我们看到了隧道的尽头。 这不是理论上的 - 在我们可以轻松地说它已准备好被广泛采用之前,我们需要完成一系列有限的事情。

以下是当今不同工作流的粗略状态:

  • <Suspense> API 用于使用lazy进行代码拆分。 (阵营16.6)

    • 您可能知道,您已经可以使用这个了。

  • 并发模式 API,例如createRootuseTransition 。 (在experimental版本中可用

    • Flux 类库的兼容性解决方案。 (正在进行@bvaughn ,https://github.com/reactjs/rfcs/pull/147)

    • 将优先级模型更改为更合理的模型。 (正在进行@acdlite ,https://github.com/facebook/react/pull/18612)

    • 只允许最后一个挂起的过渡完成。 (正在进行@acdlite)

    • 屏幕外 API(正在进行@lunaruan)

    • 隐藏/显示 Suspense 内容时的火焰效果

    • 隐藏/显示屏幕外内容时的火焰效果

    • 在需要时显示和隐藏 Portal 子项

    • 与正在进行的调度标准化工作保持一致(未开始

    • 修复主要的已知错误(正在进行@gaearon和@acdlite)

    • 更改事件语义。 (正在进行@sebmarkbage @trueadm)

    • 委托给根而不是文档以实现更渐进的采用(进行中,@trueadm)

    • 在捕获阶段刷新离散事件。

    • 考虑从event获取默认优先级,以便更好地使用命令式代码。

    • 完成其他 API 语义和默认值。 (未开始

    • 更新类型和文档。

  • 数据获取的悬念

    • 对组件发出信号的低级支持尚未准备好呈现(技术上也在稳定的 React 中使用,但该 API 不被认为是稳定的)。

    • 服务器渲染器立即刷新 Suspense 回退(在实验版本中可用

    • GraphQL 用例的解决方案(中继挂钩,已发货)。

    • 非 GraphQL 用例的解决方案(正在进行@sebmarkbage与 Next.js 合作)。

    • 数据驱动依赖项的捆绑器集成。 (进行中

    • Finalize Blocks API,包括上下文。

    • 通用缓存解决方案。 (未开始

    • 某种路由器集成。

感觉就像“它正在 Facebook 生产,所以我们完成了”。

我可以看到它看起来如何,尽管这种阅读有点令人沮丧。 :-) 在过去的几个月里,我们一直在为此不停地工作,许多技术方面要么已经完成,要么接近完成。 剩下的大部分作品分为两类:

  • 修复我们在初始设计中发现的缺陷,然后将它们固定在稳定版本中。 如果我们要发布我们现在拥有的东西,我们将不得不在几个月内进行重大的突破性更改。 那只会令人困惑。

  • 生态系统兼容性和良好的默认设置。 如果我们发布一些今天没有人可以使用的东西,那也无济于事,因为它不适用于他们的库或现有方法。 所以大部分工作(例如通过useMutableSource与 Flux 类库兼容,选择更好的事件语义,提供推荐的缓存策略)是为了确保您实际上能够使用我们发布的内容。 这是一条长尾巴。

就“您今天可以使用吗”而言……从技术上讲,您今天可以使用所有这些。 我们的确是。 具体来说,我们使用中继钩子和并发模式。 我们仍然有重大的计划更改和已知问题,因此当前状态没有达到我们认为可以广泛采用的标准。 当然,如果您不介意在您的手下更改错误或 API,欢迎您像我们一样使用@experimental版本。

我不会说 Facebook 在我们“完成”方面处于特殊地位。 恰恰相反,我们还没有完成——但在内部,我们愿意容忍流失并在移动的火车上建造,因为这就是我们知道我们正在建造的东西是可靠的。 如果没有这种沉重的狗粮,我们在六个月内发现的缺陷将需要几年时间才能发现和重新设计。

总结一下:还有更多工作要做。

所有83条评论

暴露unstable_AsyncMode (也许?)

这不是已经暴露了吗?

我的意思是删除unstable_

期待未命名代码拆分库的开源💯

【雨伞】是什么意思?🤔☂️

这意味着,它是一个影响多个项目/包/工具的功能。

@ghoullier我明白了,非常感谢!

@acdlite ,只是一个关于如何最好地准备的问题。 不是要求/期待任何类型的时间表,而是想知道:

您目前是否希望将这些功能纳入 React 16 并易于逐步采用,例如随 16.3 推出的新 Context API?

或者你认为它会推动 React 升级到 v17 并需要更多的工作来采用?

询问是因为我正在制定一个路线图,该路线图与您列表中的几乎所有内容都有显着交叉,并且我正在努力找出如何最好地处理它。

另外,您是否有任何关于如何最好地准备的技巧(就今天编写的代码而言,希望将来与 React 的这些改进兼容)- polyfills / 技术 / 等?

(如果这些问题在其他地方得到了回答而我错过了,我深表歉意)

@JedWatson的问题中添加另一个问题:

  • 我们也不需要/期望获得稳定版本的时间表,但是获得新的预发布版本可能/有用吗? (AFAIK 从 2 月份开始,最新版本是16.4.0-alpha.0911da3 。)

谢谢! ❤️

IMO,他们会像以前一样在发布之前提供一篇博客文章。

而且我认为您不需要准备太多,因为没有重大更改(它确实有许多功能可能看起来与当前实践不同/冲突,例如 redux fetch with suspense,但会有一个 codemod 或容易封装要做到这一点,你知道,fb 有 3W+ 个组件)。 如果您观看@acdlite (关于@gaearon (关于冰岛的客户端悬念)的讨论,您就会知道您不必担心太多,而且这不是侵入性的。

顺便说一句,你可以在 repo 中搜索关键的“伞”,你会发现更多信息,如 #8830 和 #12152

AFAIK 最新版本是 16.4.0-alpha.0911da3 从 2 月开始。

IIRC,这是误操作?

我正在努力在 facebook 中推出 suspense 模块和新的 API。 如果@acdlite忙于其他事情,我想分享我对我们在 facebook 上的经验的一些想法,并回答@JedWatson 的一些问题。

您目前是否希望将这些功能纳入 React 16 并易于逐步采用,例如随 16.3 推出的新 Context API?

我不确定它是否会与 React 16 或 17 一起出现。根据 React 团队的说法,它可能会在今年年底之前发布,这取决于它在 facebook 中的运行情况以及相关 API 的准备情况或不是。 但在代码方面,我很高兴地说它很容易采用,因为我们已经在 facebook 上进行了一段时间的试验。 悬念功能仍然适用于现有的代码库。 但是通过其他更改(例如异步渲染),您将获得新功能带来的更多好处。

您是否有任何关于如何最好地准备的技巧(就今天编写的代码而言,希望将来与 React 的这些改进兼容)- polyfills / 技术 / 等?

我会说迁移是相当渐进和渐进的。 就像@NE-SmallTown 所说的那样,我们不想引入任何重大更改。 推广到 facebook 也会很痛苦,因为我们有一个非常大的代码库。 但到目前为止,推出一直很顺利,不需要您进行额外的更改。

@杰德沃森

您目前是否希望将这些功能纳入 React 16 并易于逐步采用,例如随 16.3 推出的新 Context API?

渐进。 始终以增量方式进行 :) 否则我们无法在 Facebook 上发布此内容。

这是我的期望:

| | 客户 | 服务端渲染 |
|-----------------|----------------------------------------|-- -----------------------------------------|
| 悬疑| 无处不在* | 与现有服务器渲染器相同的约束|
| 异步渲染 | 选择使用<AsyncMode> | 与现有服务器渲染器相同的约束|

*在同步模式下, delayMs始终0 。 占位符立即出现。

Suspense 将在不更改现有组件的情况下工作。 一度我们认为我们可能需要<StrictMode>兼容性,但在我们的内部测试中,我们发现升级到严格模式的最佳方法之一是使用 Suspense。 鸡蛋和鸡蛋的困境。 所以我们找到了一种方法,让它在严格模式之外也能工作。

所以这个想法是用户甚至在他们准备好迁移到异步渲染之前就开始迁移到 Suspense。 然后一旦子树准备好,他们就可以通过包装在<AsyncMode>来选择加入。

但是,对于新应用程序,情况有所不同:默认情况下采用异步方式。 我们将引入一个仅异步的新根 API(替代ReactDOM.render )。

在初始发布后会有一段尴尬的时期,许多第三方框架(Redux、Apollo、React Router...)可能无法在异步模式下正常工作。 这可能会在一段时间内影响采用。 但我们的想法是,新功能将如此引人注目,以至于库很快就会适应或被异步兼容的替代方案取代。

另外,您是否有任何关于如何最好地准备的技巧(就今天编写的代码而言,希望将来与 React 的这些改进兼容)- polyfills / 技术 / 等?

将所有内容包裹在<StrictMode>并确保没有警告。 随着我们接近发布,我们将提供更详细的迁移指南。

在初始发布后会有一段尴尬的时期,许多第三方框架(Redux、Apollo、React Router...)可能无法在异步模式下正常工作。

阿波罗不尴尬 - 我们会准备好! 🕺😳

说真的,我们 :heart: React 的所有内容,因此确保我们与初始版本的这些更改保持一致不仅是一个高度优先事项,而且也是我们非常兴奋的事情! 感谢您在此@acdlite 上所做的所有出色工作!

我会插话说 Redux 团队正在为 React-Redux 开发异步兼容性。

我在https://github.com/reduxjs/react-redux/issues/950 上制定了一个潜在的路线图。 特尔;博士:

  • React-Redux 5.1 有望在没有警告的情况下与<StrictMode>一起使用(当前 PR:https://github.com/reduxjs/react-redux/pull/980)
  • 6.0 将进行内部重写以使用新的上下文 API、添加引用转发以及可能的其他更改,但尽量保留当前的公共 API(即<Provider>connect() )。 我们将看到异步渲染的效果如何,并找出前进的最佳路径。 (我之前的概念验证 PR 位于 https://github.com/reactjs/react-redux/pull/898 ,但我们可能会根据从 5.1 工作中吸取的其他经验教训重做它。)很可能这个版本至少需要 React 16.5,因为需要新的上下文,可能还有尚未发布的“从生命周期方法读取上下文”PR 刚刚合并。
  • 在那之后,我们对不同的 React-Redux API 的想法持开放态度(是的,是的,这可能包括渲染道具、人员)。

我们希望更多人关注我们的 WIP,希望人们可以就他们如何看待将 Redux 与 React Suspense 和异步渲染结合使用给我们提供更多反馈和讨论,以便我们确保正确覆盖用例。 我们还希望与 React 团队就我们需要处理的确切约束进行更多讨论,如果我们能得到一些示例应用程序,让我们看看我们需要为所有人解决什么问题,那将会很有帮助这样才能正常工作。

期待 Async 渲染和 Suspense 的发布

@acdlite还有关于悬念和异步渲染的问题。
我的问题是,一旦它们被引入并且人们开始使用新版本的 react 编写应用程序:这是否意味着 react API 和人们在 react 中编码的方式也会改变? (即使他们不打算使用悬念和异步渲染的功能?)

我认为编写带有悬念和异步渲染的 React 代码可能会更棘手(可能是由于一些新的 API 或其他限制),对于那些不需要它的人,为什么要强迫他们以新的方式使用 React? 并且不允许他们以现在的方式进行编码反应?

我认为编写带有悬念的反应代码可能会更棘手

你有机会看我的后半部分吗? 我说刚好相反-它的方式不太棘手使用悬念数据获取比什么都重要(包括终极版,本地状态,或者一些其它的库)。

@gaearon我没有。 我在理论上讲得更多。 想象一下,已经有一组知道反应的人了。 如果人们不需要异步渲染和悬念的特性,为什么要强迫他们学习“新”反应? 特别是如果“新”反应更难使用? 但是:我不是很了解所以我可能对“棘手”部分说错了 - 我只是分享我的一些想法:)。

在某种程度上我是说如果 10% 的应用程序需要 Suspense 和异步渲染的功能,为什么在其他 90% 的情况下迫使人们学习“新”反应? 但我可能又错了,因为我还没有收集到很多关于暂停和异步渲染的信息。

如果你还没有看过我的演示,我认为很难进行对话。

需要明确的是:没有“新 React”,这些功能不会破坏任何现有模式🙂。 它们是添加剂。 您也不需要以完全不同的方式编写代码来使用这些功能——尽管其中一些功能只有在您使用现代生命周期方法时才有效。

虽然这与您的担忧没有直接关系,但我不同意它们“更难使用”。 我认为 suspense 比目前存在的任何其他加载机制使用起来要简单得多。 这就是我对它如此兴奋的原因。 但同样,如果您不想旧模式将继续有效。

我真的建议看我的演讲。 我敢肯定,一旦您看到这些功能的实际应用,这会更有意义。

@gaearon

所有旧模式继续有效。

感谢您的反馈丹。 是的,这就是我的想法,我想如果人们不需要这些功能,他们应该能够像添加这些功能之前那样编写。

祝你好运。

嘿丹(@gaearon),我不是吹毛求疵,而是想弄清楚。 楼上你说:

但同样,如果您不想,您不必使用任何新功能。 旧模式将继续有效。

这表明我可以像在“旧”React 中所做的那样在新 React 中编码,例如我可以以相同的方式使用生命周期方法等,对吗?

然而,在这里,bvaughn说getDerivedStateFromProps(或componentWillReceiveProps)可以被称为多次为一个更新的,因此他的解决方案不是它里面获取数据。

所以我的问题是,毕竟,我们似乎不能像以前一样使用新的 React,对吗? 因为据我所知目前反应过来componentWillReceiveProps不会被调用多次为一个更新,是不是?

@giorgi-m :是的,生命周期方法正在发生变化,但关键是 Suspense 本身是一个选择加入的功能。 所有现有的 React 渲染方法和 React 的渲染行为都将按原样工作。 但是,_如果_您通过向应用程序的一部分添加<AsyncMode>标签来选择加入,_并且_您开始使用 Suspense 的方法来指示异步数据需求,_那么_您可以利用它。 如果您不将其添加到代码库中,则不会发生这些。

@giorgi-m componentDidUpdate应该用来代替componentWillReceivePropsgetDerivedStateFromProps

@markerikson所以你说 bvaughn在这里所说的,即 _getDerivedStateFromProps_ 可以多次调用一次更新,如果我没有启用<AsyncMode/> ,则不一定如此?
(很抱歉问这样的问题只是他们不时向我弹出,并且没有找到涵盖所有内容的资源)。

附: bvaughn 也没有提到链接线程中的可选性,因此引起了我的怀疑。

是否应该在核心清单中添加用于将异步更新排入队列的方法(例如,用于类组件的deferSetState()而不是特定于渲染器的unstable_deferredUpdates() )?

根据我的理解,异步mode纤程的任何更新都将是异步的,这在理论上意味着deferSetState()将是不必要的。 但是, unstable-async/suspense演示混合了同步更新和异步更新,我不确定如何在async模式下实现(对于“通用”组件)。

它在时间切片伞的检查清单中。

支持 promise 作为组件类型

与此相关,当您有:

const PromiseType = new Promise(() => {})
class A extends Component {
    componentDidMount() {}
    componentDidUpdate() {}
    render() {
        return <div><PromiseType></PromiseType></div>
    }
}

是否有任何关于何时调用componentDidMountcomponentDidUpdate生命周期的启发式方法。

  1. 当所有孩子都已解决(包括承诺)时,在这种情况下,这意味着他们不会被调用,因为承诺从未解决?
  2. 当所有的直接宿主孩子都被渲染了?

@thysultan :在提交阶段调用componentDidMountcomponentDidUpdate ,此时 UI 树已完全呈现并应用于 DOM。

所以,根据我对 Suspense 的理解,我认为答案是A实例永远不会真正挂载。 如果PromiseType _did_ 得到了解决,但它的一个进一步的后代也试图等待一个永远不会解决的承诺,它就永远不会挂载。 因此,在这些示例中永远不会执行cDMcDU

(如果我的假设在这里有误,有人可以随时纠正我:))

是的, componentDidMountcomponentDidUpdate仅在提交阶段执行,该整个树被解析后执行。 这棵树可能包括一些你明确放置在那里的占位符(取决于在我们等待足够长的时间后它们里面的东西是否仍然挂起)——但是如果你明确地渲染一个没有占位符的孩子,你永远不会结束它“未准备好”的情况。

我真的很期待能够玩这个(甚至浏览了很多源代码只是为了弄清楚你还没有在万维网上发布这个的工作版本)。

我们可以做些什么来帮助发布此信息? :D

如果你想玩,你可以从 master 编译。 请参阅fixtures/unstable-async/suspense

@gaearon我是否正确,这仅在其当前状态客户端(因此需要做更多工作来支持服务器端渲染)?

编辑,找到了答案:对于其他任何渴望在 SSR 中使用 Suspense 的通用应用程序的人。 我发现 Dan 的这个评论让我们知道这只是现在的客户端。 但是,该评论还指向https://www.youtube.com/watch?v=z-6JC0_cOns ,其中讨论了对 SSR 的可能影响。

我们实际上很快就会开始一些与 SSR 案例相关的工作。

想象一下在低端移动设备上运行的异步 React 应用程序中的这种场景:

  1. 有一个外部广告加载,使用所有 CPU (😓)
  2. 用户点击一个链接,路由器触发一个新的渲染
  3. React 处于异步模式,因此它会等待 Chrome/Safari 授予它使用 CPU 的权限,但广告继续加载 3 秒以上
  4. 用户认为该应用程序不起作用

使用为 Suspense 讨论的相同<Placeholder>技术可以避免这个问题,例如在 1 秒后显示一个微调器。

是否考虑过这种情况? <Placeholder>可以用于慢速异步渲染吗?

@luisherranz有两种机制可以防止这种情况:

  1. React 有一个与每次更新相关的截止日期。 来自点击和其他类似交互的更新应该在大约 150 毫秒内刷新,除非您明确选择更长的截止日期(例如,对于非必要的更新)。 因此,如果有什么东西占用了线程,React 将同步强制刷新。

  2. React 实际上不再使用requestIdleCallback了,因为浏览器在调度它方面确实不够积极。 确切的调度方法也可能会随着时间的推移而改变,但这绝对是我们关心的事情。

非常感谢丹的快速回答。

  1. React 有一个与每次更新相关的截止日期

惊人的。 是否已经有我们可以测试的 API?

  1. React 实际上不再使用 requestIdleCallback 了,因为浏览器在调度它方面确实不够积极。

这也是我们已经尝试过的。 有时,在带有外部广告并从 twitter 或 youtube 嵌入的拥挤的应用程序中,可能需要几秒钟的时间才能调用requestIdleCallback并完成渲染。 所以👍。

顺便说一句,对我们来说,还有另一个与您的第一个答案相关的用例:我们正在尝试使用具有两个偏移量的延迟加载。 第一个触发异步渲染,如果异步尚未完成,第二个触发同步渲染。 像这样的东西:

  1. 用户向下滚动,距离重元素 1200 像素:例如博客的下一篇文章。
  2. 第一个offet触发下一篇文章的异步渲染。
  3. 用户继续向下滚动,它位于下一篇文章的 600 像素处。
  4. 第二个偏移触发:如果异步帖子已经完成(componentDidMount 调用)什么也没有发生,但如果没有,它会触发整个帖子的同步渲染。

因此,我们想用第二个触发器来控制同花顺,而不是时间。 是否有意义? 可能吗?

是否已经有我们可以测试的 API?

我不确定你的意思是不是在 master 上(npm 版本中没有正式提供异步模式),但是每次更新都会自动分配过期时间。 对于像点击这样的事件,它在生产模式下约为 150 毫秒。 您无法设置它,但如果您想要使用特殊的不稳定 API,您可以选择延迟(更长)更新。

因此,我们想用第二个触发器来控制同花顺,而不是时间。 是否有意义? 可能吗?

是的,这是可能的。 我在这里描述了这样的机制: https :

我不确定你的意思是不是在 master 上(异步模式在 npm 版本中没有正式可用)

是的,我们使用带有<unstable_AsyncMode>flushSyncunstable_deferredUpdates的 npm 包。

请开始发布 alpha/beta 版本,并对 npm 进行这些更改! 🙏

是的,这是可能的。 我在这里描述了这样的机制:oliviertassinari/react-swipeable-views#453(评论)。

杰出的。 比我们目前使用unstable_deferredUpdates实现要好得多:)

等不及要开始使用<div hidden> 。 你们做得很棒。

@luisherranz小心,那些东西 _are_ 不稳定。 可能有错误或效率很低(例如,一个关键的优化——“恢复”——还没有实现)。 我们也删除了unstable_deferredUpdates以支持新的schedule模块( schedule.unstable_scheduleWork )。

是的,我们仅将它用于应用程序的一小部分并进行了密集测试,但到目前为止一切顺利:)

可能是题外话:
requestIdleCallback每秒仅被调用 20 次 - 在我的 6x2 核心 Linux 机器上的 Chrome,它对 UI 工作并不是很有用。
requestAnimationFrame被更频繁地调用,但特定于名称所暗示的任务。

出于这个原因,我们已经停止使用requestIdleCallback

你们都用什么代替requestIdleCallback

啊,当然。 傻我。 我想知道调度程序模块在后台使用什么浏览器 API 而不是requestIdleCallback 。 我应该更清楚地提出这个问题。 😅

你好,

在尝试理解 Suspense 时,我意识到在使用Suspend组件😳😱时,我不知道应用程序的哪个部分实际上是“暂停”的。

在以下示例中,我希望Title立即可见,1000 毫秒后Spinner和 ~2000 毫秒后UserData (因为“加载”该组件的数据需要 2000 毫秒)。
但我看到的是Title Spinner在 1000 毫秒后首先与

// longRunningOperation returns a promise that resolves after 2000ms
const UserResource = createResource(longRunningOperation);

function UserData() {
  const userData = UserResource.read(cache, "Lorem Ipsum");
  return <p>User Data: {userData}</p>;
}

function Spinner() {
  return <h1>Fallback Loading Spinner</h1>;
}

function Title() {
  return <h1>Hello World</h1>;
}

function App() {
  return (
    <React.Fragment>
      <Title />
      <Suspense maxDuration={1000} fallback={<Spinner />}>
        <UserData />
      </Suspense>
    </React.Fragment>
  );
}

unstable_createRoot(document.getElementById("mount")).render(<App />);

(您可以在代码和框找到使用 React 16.6.0-alpha.8af6728 的完整示例)

有没有办法让Title立即可见并“挂起”应用程序的其他部分? 还是我完全误解了悬念? (如果有更好的方式来问这类问题,请告诉我)

谢谢!

嗨@nilshartmann! 好问题!

有没有办法让 Title 立即可见并“暂停”应用程序的其他部分?

如果我理解正确,在将Title刷新到 DOM 之前,您需要明确告诉 React _not wait_,如本示例所示,以便使Title立即可见并仅“挂起”另一个通过将您希望立即呈现在<Suspense maxDuration={0}>的部分包装起来,将应用程序的一部分包装起来。

我想这是因为一些底层的低级调度机制? 我也很想更好地理解这一点,但这现在应该可以解决您的问题。

我很高兴听到那里发生了什么。

(如果有更好的方式来问这类问题,请告诉我)

我不确定有没有。 😄 对我来说似乎很清楚。 谢谢提问!

@TejasQ在我的浏览器中,加载您的示例会立即呈现后备微调器。 它不应该在 1000 毫秒后加载吗?

@TejasQ感谢您的回答,但@karlhorky是对的:现在微调器立即出现。

哎呀! 我错过了。 我试过。 😅让我再看看它,然后再回复你。 我一定错过了什么。 🤷‍♂️

〜更新:我正在尝试在这里弄清楚,如果有人有兴趣实时一起做,我们可以合作。〜

第二次更新: @philipp-spiess,我看过它,我真的被难住了。 我还是不明白。 在这一点上,我不确定这是否是一个错误,因为这实际上是一个unstable_和 alpha 功能,或者它是否是我根本没有看到的东西。

在任何一种情况下,我觉得核心团队要么有有用的答案,要么能够使用您的问题使 React 变得更好/更平易近人。

让我们看看他们怎么说。 😄 感谢您指出这一点,@nilshartmann!

这是作为 React v16.6 的一部分发布的吗? 博文展示了使用 Suspense 的示例代码:

import React, {lazy, Suspense} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));

function MyComponent() (
  <Suspense fallback={<div>Loading...</div>}>
    <OtherComponent />
  </Suspense>
);

这是作为 React v16.6 的一部分发布的吗?

仅延迟加载用例,并且仅在同步模式下。 并发模式仍然是 WIP。

@尼尔夏特曼

在下面的示例中,我希望 Title 立即可见,Spinner 在 1000ms 之后,UserData 在 ~2000ms 之后(因为“加载”该组件的数据需要 2000ms)。

我确实认为您对maxDuration作用有点困惑。 这是一个新的心智模型,但我们还没有时间记录下来。 因此,在并发模式处于稳定版本之前,它会持续混淆一段时间。

恭喜宣布 hooks 提案。 我想与团队分享一些东西。 不久前,我发布了一个名为React Async的组件,它具有类似于 Suspense 的功能。 本质上,它处理 Promise 解析,并提供诸如 isLoading、startedAt 之类的元数据以及诸如 reload 和 cancel 之类的方法,所有这些都带有声明性 API(并且正在开发一个useAsync 钩子)。

现在我主要关心的是这将如何与 Suspense 集成。 在大多数情况下,我可能可以使用 React Async 的 Suspense API,并为用户提供熟悉且简单的 React Async API,同时免费提供 Suspense 的调度功能。 就我所见,我真诚地认为 React Async API 与更抽象的 Suspense API 相比更明智、更平易近人。 从本质上讲,React Async 试图提供一个更具体的 API,该 API 适用于较小的用例子集。

我很惊讶地了解到 React Cache 库。 对于 React Async,我故意不包含缓存机制,而是选择处理 vanilla Promises。 在此基础上添加缓存相当容易。

最后,我担心从自定义钩子访问 Suspense 功能。 Suspense 似乎严重依赖于几个内置组件,这使得(我认为?)从钩子中使用这些组件是不可能的。 会有悬念挂钩吗? 或者有什么其他方法可以将两者结合起来?

你好。 如何使用 Suspense/Lazy 测试代码?
现在 renderer.create(...)toTree() 抛出
“toTree() 还不知道如何处理 tag=13 的节点”

为什么Suspense的 props maxDuration只在Concurrent Mode而不是同步和并发模式都使用。 谁能帮忙解释一下?

(现在)它意味着在强制提交之前允许Concurrent Mode使这棵树挂起多长时间——它有效地控制了时间切片的截止日期,并且在同步模式下不存在时间切片。 在提交树之前等待必然会使提交......而不是同步。

我一直在内部应用程序中使用 Suspense 进行数据提取,很快就发现了为什么它不打算用于数据提取的原因。

但最终,它旨在用于获取数据。 鉴于除了缓存提供程序之外,API 似乎不太可能发生重大变化,如果您在获取数据后需要修改数据,那么 Suspense 将如何工作?

例如,这是我的应用程序中一个非常糟糕的钩子。

function useComponentList(id) {
  const incomingComponents = useSuspenseFetch(
    React.useCallback(() => getComponentAPI().listComponents(id), [id])
  )

  const map = React.useMemo(
    () =>
      Map(
        (incomingComponents || []).map(component => [component.id, component])
      ),
    [incomingComponents]
  )

  return useCacheValue(map)
}

这个钩子:

  1. 使用给定的回调从给定的端点获取数据
  2. 将该数据转换为 ImmutableJS Map - 由于这可能很昂贵,所以我记住了该操作。
  3. 返回包裹在useCacheValue中的地图,这是特别尴尬的一点。

useCacheValue看起来像这样:

export default function useCacheValue(value) {
  const [state, setState] = React.useState(value)
  React.useEffect(() => {
    setState(value)
  }, [value])

  return [state, setState]
}

其想法是它是一个钩子,将响应value更改(表示数据已重新获取),但允许用户修改该状态的反应应用程序表示。 在某种程度上,它就像一个非常糟糕的缓存(因此得名)。

我正在努力了解这种模式在当前状态下如何与 Redux 配合使用。 当由不是我的程序员编写并且悬念“准备好”获取数据时,是否有任何发现? 就目前而言,这比单独使用 Redux 和命令式获取标志要费力得多。

一旦Redux 有了自己的钩子,这可能会变

Suspense 旨在与外部缓存(而不是由状态驱动的 Hook)一起使用。 我们将提供适用于简单用例的此类缓存的参考实现。 Relay 将实现自己的缓存机制。 任何其他工具(例如 Apollo)也可以实现自己的兼容缓存,这可能会受到这两种实现的启发。

突变/失效并不是唯一需要回答的问题。 我们还需要考虑:何时显示微调器、可能在挂起树之外的“内联指示器”等常见模式、协调加载状态(对于需要按自上而下的顺序解锁或按原样进入的事物)准备好),列表的流式渲染,这如何影响部分水合作用,等等。 我们正在研究这些事情,但目前还没有关于其中任何一项的“官方建议”。 如果有,您会从我们发布更新的博客中得知。

作为旁注,用于数据获取的 Suspense 是一种与人们可能习惯的思维模型截然不同的思维模型。 我认为在与 Redux 等非常不受约束的机制集成时,期望它一定会如此强大是不公平的。 但我们会看到的。 现在很难说什么。

@gaearon当您说“我们正在处理这些事情”时,是否有我可以订阅的问题,或者这些讨论是否私下进行?

谢谢, @gaearon :)

@ntucker与往常一样,您可以作为 PR 观看正在进行的活动。 例如: https : //github.com/facebook/react/pull/14884、https : //github.com/facebook/react/pull/15061 , https://github.com/facebook/react/pull/15151 , https://github.com/facebook/react/pull/15272 , https://github.com/facebook/react/pull/15358 , https ://github.com/facebook/react/pull/15367等等。 我们尝试在每个 PR 中放入一些描述性信息,您可以从测试中看到行为变化。 出于多种原因,实验的文档栏很低。

在我们更加确信它确实有效后,我们将发布有关所选模型的更完整的解释。 如果我们在每个实验发生时煞费苦心地详细描述它,我认为这对我们或社区都没有成效。 我们最初的大多数实验都失败了,记录和解释每一个实验会使我们的工作慢下来。

更糟糕的是,这通常会导致人们围绕我们后来意识到无法以最初设计的方式工作的事物建立心理模型。 (就像我们刚刚删除的maxDuration发生的情况一样。)所以我们宁愿推迟分享半成品的想法,直到它可以很好地利用您和我们的时间。 这也与我们过去开发 React 的方式一致。 当某些东西真正准备好时(即使是对心智模型的理论写作),我们会将所有注意力集中在记录和解释它上。

作为旁注,用于数据获取的 Suspense 是一种与人们可能习惯的思维模型截然不同的思维模型。 我认为在与 Redux 等非常不受约束的机制集成时,期望它一定会如此强大是不公平的。 但我们会看到的。 现在很难说什么。

@gaearon ,幸运的是,Suspense 的心智模型与我自己的完美匹配。 对那块拼图落到位感到非常兴奋。 感谢您的辛勤劳动!

去年 11 月公布的路线图 (https://reactjs.org/blog/2018/11/27/react-16-roadmap.html) 表明 Suspense 的“并发”版本定于 2019 年第二季度。我们现在很好2019 年第 3 季度。我们是否可以获得绝对不是 Q3 或 Q3 等方面的更新?

这是我能找到的最新路线图更新: https: //reactjs.org/blog/2019/08/08/react-v16.9.0.html#an -update-to-the-roadmap

我们在 10 月份提供了一个实验性版本: https :

悬念杀了我

@gaearon我知道您在生产中使用它。 但我非常不愿意在生产中使用“实验性”代码。 你们不清楚路线图、状态、进度、时间等。这是 alpha、beta、RC 质量吗? “实验性”这个词对我说“不要碰这个”。

我们都把我们的业务寄托在这个代码上,我相信我们都和你们一样被淹没了。 稍微清晰一点,一个博客,一些东西真的会有所帮助。 感觉就像“它正在 Facebook 生产,所以我们完成了”。

@mschipperheyn

这是一个多年的项目。 诚实的回答是,它产生的工作比我们两年前开始时想象的要多。

但好消息是,因为我们现在在生产中大量使用它,缺失的部分很清楚,我们看到了隧道的尽头。 这不是理论上的 - 在我们可以轻松地说它已准备好被广泛采用之前,我们需要完成一系列有限的事情。

以下是当今不同工作流的粗略状态:

  • <Suspense> API 用于使用lazy进行代码拆分。 (阵营16.6)

    • 您可能知道,您已经可以使用这个了。

  • 并发模式 API,例如createRootuseTransition 。 (在experimental版本中可用

    • Flux 类库的兼容性解决方案。 (正在进行@bvaughn ,https://github.com/reactjs/rfcs/pull/147)

    • 将优先级模型更改为更合理的模型。 (正在进行@acdlite ,https://github.com/facebook/react/pull/18612)

    • 只允许最后一个挂起的过渡完成。 (正在进行@acdlite)

    • 屏幕外 API(正在进行@lunaruan)

    • 隐藏/显示 Suspense 内容时的火焰效果

    • 隐藏/显示屏幕外内容时的火焰效果

    • 在需要时显示和隐藏 Portal 子项

    • 与正在进行的调度标准化工作保持一致(未开始

    • 修复主要的已知错误(正在进行@gaearon和@acdlite)

    • 更改事件语义。 (正在进行@sebmarkbage @trueadm)

    • 委托给根而不是文档以实现更渐进的采用(进行中,@trueadm)

    • 在捕获阶段刷新离散事件。

    • 考虑从event获取默认优先级,以便更好地使用命令式代码。

    • 完成其他 API 语义和默认值。 (未开始

    • 更新类型和文档。

  • 数据获取的悬念

    • 对组件发出信号的低级支持尚未准备好呈现(技术上也在稳定的 React 中使用,但该 API 不被认为是稳定的)。

    • 服务器渲染器立即刷新 Suspense 回退(在实验版本中可用

    • GraphQL 用例的解决方案(中继挂钩,已发货)。

    • 非 GraphQL 用例的解决方案(正在进行@sebmarkbage与 Next.js 合作)。

    • 数据驱动依赖项的捆绑器集成。 (进行中

    • Finalize Blocks API,包括上下文。

    • 通用缓存解决方案。 (未开始

    • 某种路由器集成。

感觉就像“它正在 Facebook 生产,所以我们完成了”。

我可以看到它看起来如何,尽管这种阅读有点令人沮丧。 :-) 在过去的几个月里,我们一直在为此不停地工作,许多技术方面要么已经完成,要么接近完成。 剩下的大部分作品分为两类:

  • 修复我们在初始设计中发现的缺陷,然后将它们固定在稳定版本中。 如果我们要发布我们现在拥有的东西,我们将不得不在几个月内进行重大的突破性更改。 那只会令人困惑。

  • 生态系统兼容性和良好的默认设置。 如果我们发布一些今天没有人可以使用的东西,那也无济于事,因为它不适用于他们的库或现有方法。 所以大部分工作(例如通过useMutableSource与 Flux 类库兼容,选择更好的事件语义,提供推荐的缓存策略)是为了确保您实际上能够使用我们发布的内容。 这是一条长尾巴。

就“您今天可以使用吗”而言……从技术上讲,您今天可以使用所有这些。 我们的确是。 具体来说,我们使用中继钩子和并发模式。 我们仍然有重大的计划更改和已知问题,因此当前状态没有达到我们认为可以广泛采用的标准。 当然,如果您不介意在您的手下更改错误或 API,欢迎您像我们一样使用@experimental版本。

我不会说 Facebook 在我们“完成”方面处于特殊地位。 恰恰相反,我们还没有完成——但在内部,我们愿意容忍流失并在移动的火车上建造,因为这就是我们知道我们正在建造的东西是可靠的。 如果没有这种沉重的狗粮,我们在六个月内发现的缺陷将需要几年时间才能发现和重新设计。

总结一下:还有更多工作要做。

@gaearon感谢您的更新! 如果我的语气有误,我深表歉意。 我承认我在 Twitter 上搜索了几个月却一无所获,有点沮丧。 这个方面Server renderer immediately flushes Suspense fallbacks (available in experimental releases)看起来我可以分配时间用 Apollo Graphql 测试我们的实现。

是的,它应该已经准备好供图书馆作者和好奇的人开始玩了。 缺失的部分主要是关于提供“快乐路径”,但大部分管道应该在那里。

服务器渲染器立即刷新 Suspense 回退(在实验版本中可用)

我在哪里可以阅读有关此内容的信息? 我希望并发模式 API 参考(实验)但没有运气。

如果有人有拼接 Next.js、Relay Hooks 和并发模式(使用 SSR)的演示,那就太棒了。 否则,如果我能找到足够的文档,我可能会试试运气。

@CrocoDillon

没有关于 SSR 的额外文档,但这主要是因为它只是一个新的默认行为。

如果您有一个实验性版本,而不是组件在服务器上挂起的任何时间,我们都会刷新最近的 Suspense 回退。 然后在客户端上,您将使用createRoot(node, { hydrate: true }).render(<App />)

请注意,这已经启用了所有新的补水功能。 例如,您的 Suspense 边界将“附加”到服务器生成的回退 HTML,然后尝试客户端呈现。

另请注意,您可以整个应用程序加载<App>加载完毕后,您就可以补水了。 只要下面的组件在其代码未准备好时暂停(类似于惰性)。 在这种情况下,React 会做的是保留服务器 HTML 内容,但将 Suspense 边界“附加”到它上面。 当子组件加载时,它将继续保湿。 水合部分将成为互动和重播事件。

您可能可以向@devknoll询问下一次集成尝试/示例。 他大概有一些。

您可以通过安装react@experimentalreact-dom@experimental并将以下内容添加到next.config.js来启用 Next.js 中的并发模式

// next.config.js
module.exports = {
  experimental: {
    reactMode: 'concurrent'
  }
}

这是 Next.js 对此的讨论: https :

是否可以等待服务器渲染中的任何悬念(例如静态站点生成等情况)? 我同意使用回调是一个很好的默认设置,只是想知道它是否可以覆盖?

另请注意,您可以在整个应用程序加载之前开始补水。 什么时候装好了,就可以补水了。 只要下面的组件在其代码未准备好时暂停(类似于惰性)。 在这种情况下,React 会做的是保留服务器 HTML 内容,但将 Suspense 边界“附加”到它上面。 当子组件加载时,它将继续保湿。 水合部分将成为互动和重播事件。

@gaearon你的意思是你可以在服务器上正常渲染一个组件,但在客户端使用 React.lazy 吗? 允许您从服务器返回完整标记,但延迟客户端上组件代码的解析和呈现。 服务器呈现的标记在这里充当了悬念后备?

@robrichard我实际上并没有专门用React.lazy尝试过(我们在 FB 使用不同的包装器,Next 也有自己的版本),但我希望这就是它的工作方式。 值得验证 :-) 有一些限制——例如,如果你更新了它的 props 并且上面没有备忘录救助,或者如果你更新它上面的上下文,我们将不得不删除它并显示后备,因为我们不知道该怎么做用它。

@gaearon部分水合的当前状态是什么? 我知道 #14717 已合并,但我怀疑它是否已发布到任何版本中?

只要您使用unstable_createRoot API,它已经在每个@experimental版本中存在很长时间了。

这是一个演示: https : index.js

@gaearon你能详细说明你所说的“数据驱动的依赖关系”是什么意思吗?

@gaearon你能详细说明你所说的“数据驱动的依赖关系”是什么意思吗?

当然是。 我必须为上面列表的简短而道歉——它非常简洁,其中许多是重要的独立子项目(这就是他们花时间的原因)。

在回答您的具体问题之前,让我退后一步并提供一些更广泛的背景。 更广泛的背景是,构建一个真正好的数据获取解决方案真的非常非常困难。 不仅在实施的意义上,而且在设计的意义上。 通常,人们必须在托管(将数据需求保持在数据使用位置附近)和效率(我们多早开始加载数据,以及我们能否避免网络瀑布)之间做出折衷。 这在小规模上并不明显,但随着组件数量的增加,您确实必须在出色的性能和易于编写的代码之间做出选择。 不幸的是,在许多情况下,您两者都得不到——这就是为什么数据获取通常是一个如此热门的话题。

对于进入“官方”React 的内容,我们有很高的标准。 要成为“Reacty”,它必须像常规 React 组件一样进行组合。 这意味着我们不能真诚地推荐一个我们认为不会扩展到数千个组件的解决方案。 我们也不能推荐强制您以复杂的优化方式编写代码以保持其性能的解决方案。 在 FB,我们从 Relay 团队那里学到了很多这方面的经验。 我们知道不是每个人都可以使用 GraphQL,或者想要使用 Relay(它本身并不是很受欢迎,团队还没有针对外部采用对其进行优化)。 但是我们希望确保我们的通用 React 数据获取解决方案结合了Relay

我想强调这不仅仅是关于大型应用程序。 这些问题在大型应用程序中最为明显,但从 npm 导入大量组件的小型应用程序也存在这些低效问题的一部分。 例如传送过多的客户端代码并将其加载到瀑布中。 或者预先加载太多。 此外,小应用程序不会永远成为小应用程序。 我们想要一个适用于任何规模的应用程序的解决方案,就像 React 组件模型无论您的应用程序的复杂性如何都以相同的方式工作。

现在,解决您的具体问题。 Relay 有一个特性叫做“数据驱动的依赖”。 考虑它的一种方法是动态import的演变。 动态import并不总是有效的。 如果您只想在条件为真时加载某些代码(例如“用户是否已登录”或“用户是否有未读消息”),您唯一的选择就是延迟触发它。 但这意味着您只有在使用某些东西时才“开始”获取(例如使用React.lazy )。 这其实为时已晚。 例如,如果您有多个级别的代码拆分组件(或等待数据的组件),那么最里面的一个只会在它上面的一个加载之后才开始加载。 这是低效的,是一个网络“瀑布”。 中继“数据驱动的依赖关系”让您可以指定要获取的模块作为查询的一部分。 即“如果某些条件为真,则将此代码块包含在数据响应中”。 这使您可以尽早加载您将需要的所有代码拆分块。 您不必用主机托管来换取性能。 这似乎没什么大不了的,但它减少了产品代码中的字面意思。

现在,再次重申,我们不会将 Relay 放入 React,我们也不想强迫人们使用 GraphQL。 但从概念上讲,该功能本身是通用的,并且拥有一个良好的开源解决方案可以让人们进行比今天更多的代码拆分(因此交付的客户端 JS 代码要少得多!)该通用功能将不会被调用“数据驱动的依赖关系”——这只是我提到的中继名称。 此功能将成为不需要 GraphQL 的更大推荐解决方案的一部分。 我只是在列表中以该名称提及它,因为对于单个项目符号列表点来说,这是很多解释。

我希望这能澄清它。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

sebmarkbage picture sebmarkbage  ·  81评论

brunolemos picture brunolemos  ·  285评论

sebmarkbage picture sebmarkbage  ·  136评论

wohali picture wohali  ·  128评论

sophiebits picture sophiebits  ·  107评论