Facebook 将对 React 的优化实现到了浏览器!

想要提高一个网页的加载速度是非常困难的,如果你的网站是在使用 JavaScript 渲染的内容,你必须要在网页的加载速度和网页的输入响应能力之间作出权衡:

  • 一次性执行首屏需要执行的逻辑(负载性能好,输入响应能力差)

  • 将复杂的逻辑拆分成更小块的任务执行,以保证对外界输入的响应(负载性能差,输入响应能力好)

为了避免这种取舍,Facebook 在 Chromium 中提出并实现了 isInputPending() API,它可以提高网页的响应能力,但是不会对性能造成太大影响。

目前 isInputPending API 仅在 Chromium 的 87 版本开始提供,其他浏览器并未实现。

背景

在现今的 JavaScript 生态中,大多数工作都是在一个线程完成的:主线程。这种设计为开发者提供了一个健壮的执行模型,但是如果脚本执行的时间太长,则用户体验(尤其是响应能力)可能会遭受严重损失。例如,用户正在输入一些内容时, JavaScript 正在执行大量的逻辑,则在这些逻辑完成之前,浏览器都不能处理用户的输入事件。

现在的最佳实践是通过将复杂的逻辑拆分成更小块的任务执行来解决这种问题。在页面加载期间,页面可以运行一些 JavaScript 逻辑,然后将控制权转交给浏览器,这时浏览器可以检测自己的事件队列,看看是不是需要响应用户输入,然后再继续运行 JavaScript ,这种方式虽然会有一些帮助,但是同时也可能会带来其他问题。

每次页面将控制权交还给浏览器时,浏览器都会花费一些时间来检查它的事件队列,处理完事件后再获取下一个 JavaScript 代码逻辑。当浏览器更快地响应事件时,页面的整体加载时间会变慢。而且,用户输交互比较多的情况下,页面加载会非常慢。如果我们不那么频繁地进行上面的过程,那么浏览器响应用户事件所花费的时间就会更长。

Facebook 提出的 isInputPending API 是第一个将中断的概念用于浏览器用户交互的的功能,并且允许 JavaScript 能够检查事件队列而不会将控制权交于浏览器。

下面我们来具体看一个例子。

一个例子

假设您需要做很多显示阻塞的工作来加载页面,例如,从组件生成标记,分解质数或仅绘制一个很酷的加载微调器。这些中的每一个都分解为一个离散的工作项。使用调度程序模式,让我们勾勒出如何在假设的processWorkQueue()函数中处理我们的工作:

假设你再首屏加载页面时要处理非常多的阻塞逻辑,例如从组件生成标记,分解质数,或者只是绘制一个很酷的加载器动画。这些逻辑都会被分解成一个独立的工作项。使用 scheduler 模式,让我们在一个假设的 processWorkQueue() 函数中处理我们的逻辑:

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (performance.now() >= DEADLINE) {
    // Yield the event loop if we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

通过 processWorkQueue() 延时链式调用 setTimeout(),我们使浏览器能够在某种程度上保持对输入的响应,同时仍然在相对不间断地运行。但是,可能需要很长时间才能完成其他需要控制事件循环的工作,或者使 QUANTUM 事件延迟增加一毫秒。

在RAIL模型下,QUANTUM 一个好的值是<50ms,这取决于要完成的工作类型。该值主要决定了处理能力和延迟之间的平衡。

但是,我们还可以做的更好:

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event, or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

通过调用 navigator.scheduling.isInputPending(),我们可以更快地响应输入,同时仍然确保我们的阻塞逻辑能够不间断地执行。如果在完成这些逻辑之前对用户交互(例如绘画)以外的其他操作不感兴趣,则可以方便地增加输入的长度 QUANTUM

默认情况下,“连续”的事件类型不会返回 isInputPending(),比如 mousemovepointermove 等等。如果你对这些也感兴趣的话,没问题。可以通过为 isInputPending() 提供一个包含连续变量为true的字典:

const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event (any of them!), or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

像 React 这样的框架正在将 isInputPending() 使用类似的逻辑构建到其核心调度库中。希望这将使使用这些框架的开发人员能够从幕后的 isInputPending() 中受益,而不要进行重大的重写。

React Fiber

让我们回想下 React Fiber 中的时间分片:

把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。

React Fiber 把更新过程碎片化,执行过程如下面的图所示,每执行完一段更新过程,就把控制权交还给 React 负责任务协调的模块,看看有没有其他紧急任务要做,如果没有就继续去更新,如果有紧急任务,那就去做紧急任务。

如果你对该库感兴趣,可以到 https://github.com/WICG/is-input-pending参与反馈和讨论。

参考:https://web.dev/isinputpending/

不得不说 React 团队还是非常强的,一个 JavaScript 库能带动浏览器的发展。虽然这还是第一个由 Facebook 贡献给浏览器的能力,不过未来可期,让我们期待更多更强大的 API 吧!

关于奇舞精选

《奇舞精选》是360公司专业前端团队「奇舞团」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。

奇舞团是360集团最大的大前端团队,代表集团参与W3C和Ecma会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队Leader等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将React Native应用程序运行到浏览器中,可以使用React Native Web库,它将React Native组件转换为Web组件,从而使其可以在Web平台上运行。 以下是将React Native应用程序运行到浏览器的步骤: 1. 首先,需要在React Native应用程序中安装React Native Web库。可以使用以下命令进行安装: ``` npm install react-native-web ``` 2. 在应用程序的入口文件(例如App.js)中,导入React Native Web库,并将其与React Native应用程序一起导入: ``` import React from 'react'; import {AppRegistry} from 'react-native'; import {name as appName} from './app.json'; import App from './App'; import {AppContainer} from 'react-hot-loader'; import {enableScreens} from 'react-native-screens'; import {createBrowserApp} from '@react-navigation/web'; import './platform-specific/browser.polyfill'; import {setJSExceptionHandler} from 'react-native-exception-handler'; import * as Sentry from '@sentry/react-native'; import {Platform} from 'react-native'; import 'react-native-gesture-handler'; import {Provider} from 'react-redux'; import {PersistGate} from 'redux-persist/integration/react'; import {store, persistor} from './store'; import {ThemeProvider} from 'styled-components/native'; import {theme} from './styles/theme'; import {UIProvider} from './contexts/UIContext'; import ErrorBoundary from './components/ErrorBoundary'; enableScreens(); if (!__DEV__) { Sentry.init({ dsn: '...', }); } const AppNavigator = createBrowserApp(App); AppRegistry.registerComponent(appName, () => { setJSExceptionHandler((error, isFatal) => { if (isFatal) { Sentry.captureException(error); } }); const render = (Component) => { const renderMethod = module.hot ? ReactDOM.render : ReactDOM.hydrate; renderMethod( <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <ThemeProvider theme={theme}> <UIProvider> <ErrorBoundary> <AppContainer> <Component /> </AppContainer> </ErrorBoundary> </UIProvider> </ThemeProvider> </PersistGate> </Provider>, document.getElementById('root'), ); }; render(AppNavigator); if (module.hot) { module.hot.accept('./App', () => { const NextApp = require('./App').default; render(createBrowserApp(NextApp)); }); } }); ``` 3. 然后,需要创建一个HTML文件,将其引入到React Native应用程序的入口文件中,并将React Native应用程序渲染到HTML文件中: ``` <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>React Native Web</title> <script src="./dist/main.bundle.js"></script> </head> <body> <div id="root"></div> </body> </html> ``` 4. 最后,需要使用Webpack或类似的工具将React Native应用程序打包为可在浏览器中运行的JavaScript文件。可以使用以下命令进行打包: ``` webpack --config webpack.config.js ``` 完成以上步骤后,即可将React Native应用程序运行到浏览器中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值