Redux 是一个状态管理工具,它可以让开发者更容易地管理前端应用的状态。然而,在实际应用中,随着应用的复杂度不断提升,Redux 在服务端渲染(SSR)和同构渲染方面存在一些问题,可能会引起一些性能和可维护性方面的挑战。在本文中,我们将介绍如何在同构渲染中使用 Redux,并提供优化方案,以获得更好的性能和开发体验。
常规 Redux 在 SSR 中存在的问题
在 SSR 中,数据应该在服务端先加载,在 HTML 发送给客户端之前注入到页面中,以增强应用的性能和用户体验。然而,Redux 的默认方式是将状态存储在客户端,在客户端渲染完整个页面后再加载数据。因此,在同构渲染中使用 Redux 需要考虑以下问题:
- 首次渲染时页面将会较慢。 页面的首次渲染可能需要等待请求发送服务器,等待服务器返回并解析数据或等待其他客户端逻辑完成。
- 数据加载可能会被重复进行。 首次请求会在服务器端实现,多次请求会在客户端重复进行。
- 请求和渲染的顺序可能会导致 UI 闪烁。 如果数据请求与渲染不同步,那么,页面很有可能会闪烁,用户体验差。
Redux 同构渲染实战
针对上述问题,我们可以借助一些技术手段解决:
服务器端渲染
用服务器渲染来提升页面性能。首先,我们需要对 React 应用进行 SSR。可以借助第三方库,如 Next.js
和 Gatsby.js
,它们提供了丰富的配置,可以将应用程序的页面完全渲染到 HTML、CSS 和 JavaScript 中,并且这些页面很容易高效缓存。
------ ----- ---- ------- ------ - -------------- - ---- ------------------ ------ - -------- - ---- ------------- ------ --- ---- ------- ----- ------- - ------------------ ----- --- - --------- ----- ---- - ---- ------------ ----- ---- -- - ----- ----- - ---------------- ----- ---- - --------------- --------- -------------- ---- -- ----------- - ---------- --------- ----- ------ ------ ------------ --- --------------- ------- ------ ---- ----------------------- -------- ------------------------ - ------------------------------------ --------- ------- ------------------------ ------- ------- -- -- ---------------- -- -- - ------------------- -- --------- -- ---- --------- --
如上所示,我们可以在服务器端将应用程序的碎片完全渲染到 HTML、CSS 和 JavaScript 中,并将初始状态序列化到内联脚本中。当页面首次加载时,客户端 JavaScript 可以使用这个状态,从而不需要再次加载数据。
开发同构 Action
Next.js 和 Gatsby.js 能在服务器和客户端公用相同的代码和组件。如果常规的 Redux 中间件被用来处理在客户端中产生的 Action,例如通过触发 Redux 操作更改路由,不能工作的事件,那么同一个 Action 会在服务端重复发送。
一种方式是“请求桥梁”或“Action 桥梁模式”。我们为每个 Action 创建一个服务器端方法。这些方法由客户端代码调用,它们会在服务端异步执行相应的 Action。
-- ---------------------- ------ ----- ---------- - ------------- ------ ----- --------- - -- -- -- ----- ---------- --- -- ---------------------- ------ ----- --------- - -- -- ----- ---------- --------- -- - ----- ---- - ----- ------------- ---------- ----- ----------- -------- ---- --- --
在服务端 Rehydration 状态
在客户端 JavaScript 加载时,Redux 的初始状态是从服务器端渲染中服务端使用 JSON.stringify()
序列化的对象中。我们需要 React 同构渲染提供的“hydrate”方法,使这些更新和重新填充需要更新的组件,而不需要重复渲染应用程序。
------ ----- ---- ------- ------ - ------- - ---- ----------- ------ - -------- - ---- ------------- ------ --- ---- ------- ------ -------------- ---- ------------------------ ----- ----- - ---------------------------------------- -------- --------- -------------- ---- -- ------------ ------------------------------- -
Server-side Rendering 期间消除无关代码
在服务器端,我们可以根据初始状态编写精简版本的 reducer。我们可以使用 Redux 提供的 createStore
函数,在每个页面请求时生成不同的 store。
-- -------------------------- ------ - ----------- - ---- ------- ------ ----------- ---- -------------------- ------ - --------- - ---- ----------- ------ - ------------- ------------ - ---- ------------------- ----- ------------- - -- -- - ------ --- ----------------- ------- -- - ----- ----- - ------------------------ ---------------- ----- ------------ -- ----- ---------------------- -------- -- - ---------------- ----- ------------ -- -------------- -- ---------- -- ------------ -- - ------ ------- --------------
当我们在服务端渲染过程中执行 loadDataStore
时,我们只需要根据页面请求中的 url,选择需要在我们的组件中展示哪个 reducer。在组件渲染完成后,可以简化为只保留与当前页面相关的(例如选定的页面或板块),从而令处理程序速度更快。
在客户端启用预加载
要减少客户端 JavaScript 加载时间,我们可以使用 React suspense。 一旦页面获得客户端的控制权时,我们可以使用预加载,从而在加载视图时同时加载所需的组件和数据。
-- ------------------ ------ ----- ---- ------- ------ - ------- - ---- ------------- ------ - --------- - ---- ----------- ----- ---------- - ------------- -- ------------------------ ----- ---------- - ------------- -- ------------------------ ----- --- ------- --------------- - ------------------- - ----- - --------- - - ---------- ----------- - -------- - ------ - --------------- --------------------------------- ----------- -- ----------- -- ----------------- - - - ----- ------------------ - ---------- -- - ------ - ---------- -- -- - --------------------- - - - ------ ------- ------------- -------------------------
如上所示,当客户端代码首次加载时,预加载选定的组件。 这样,当客户端 JavaScript 得到控制并生成组件时,预加载组件中的数据立即可用。
优化方案
虽然 Redux 应用程序可以进行 SSR,但在实际应用中性能问题会很常见。这是由于同一数据可能会在客户端和服务器上进行多次请求,并且大多数时候这些请求和渲染的顺序都没有被优化,导致页面 UI 闪烁和性能问题。下面是一些可以优化的方案。
使用 Redux 动态模块加载(Code Splitting)
对于大型应用程序,需要使用动态模块加载来拆分代码模块并更改需加载的方式。 这大大缩短了页面加载时间。 Webpack 4 支持用于代码分割创建动态导入的语法,可以使其更容易使用。
------ - ----- -------- - ---- -------- ----- -------------- - ------- -- ---------------------------- -------- ------------- - ------ - ----- --------- --------------------------------- --------------- -- ----------- ------ -- -
如上所示,你可以延迟加载任何组件或资产,这将使页面更快。 让你的客户端跑得更快。
使用强制缓存
为了减少服务器响应时间并提高性能,可以启用“强制缓存”,这样服务器不必在每个页面请求时都发送相同的内容。通常情况下,我们可以利用 Webpack 的缓存分离来分离缓存文件。
----- ---- - ---------------- ----- ------------- - ---------------------------------- -------------- - - ------ ------------ ------- - ----- ----------------------- -------- --------- ---------- -- -------- ---- ---------------------------- --
如上所示,我们启用了 workbox-webpack-plugin
插件来自动生成 Service Worker,其中结合了“缓存格外的查找”策略,用户第一次访问 Web 应用程序时返回静态内容,然后缓存。 下次客户端资源请求将直接从缓存中获取,而不是从服务端再次获取。
结论
在本文中,我们介绍了如何在同构渲染中使用 Redux,并提供了一些优化方案,以获得更好的性能和开发体验。虽然同构渲染在一些方面存在挑战,但使用上述技术可使我们更简单地处理 Redux 在服务端渲染和同构渲染方面的问题。 了解这些技术将帮助开发人员更好地掌握 Redux 的能力,用它来构建可维护的大型应用程序,并提高 Web 应用程序的性能和用户体验。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/672ed790eedcc8a97c8b01ac