React/Redux同构/服务器端渲染和媒体查询

7

我开始创建一个基于Node的同构React/Redux应用程序。该项目的要求之一是根据“移动”和“桌面”视图进行特定组件的“自适应”渲染。我已经实现了Redux action和reducer来存储关于用户视图的屏幕信息(基于媒体查询 - “小”,“中”,“大”)在状态中。调整大小时,状态/存储会得到更新。默认状态为“小”。

const defaultState = {
    isMobile: true,
    isTablet: false,
    isDesktop: false,
    sizes: {
        small: true,
        medium: false,
        large: false,
        huge: false,
    },
};

在需要根据屏幕尺寸"自适应"渲染两个不同版本的组件中,我只需这样做:

如果 (小屏) 返回变化1

如果 (中屏) 返回变化2

一切运作正常。
现在我面临两个问题:
  1. 我的应用程序是同构的,这意味着标记也会在服务器端呈现。服务器不知道用户的浏览器和媒体查询信息。因此,由于我的默认状态是"小屏",服务器将始终呈现"变化1"。节点服务器是站点的入口点。看起来呈现需要被"延迟"(中间件?),并且服务器需要在React应用程序被"传递"之前从客户端获取有关浏览器宽度的一些信息。您有任何解决方案吗?
  2. 由于呈现是基于状态的,因此加载后"变化1"可能始终在前几毫秒内(闪烁)可见,即使浏览器尺寸是"桌面"。这是因为JS检测在状态更新为当前屏幕宽度之前需要几毫秒时间。我认为这与上述问题和默认状态有关。
我找不到解决方法1,但我想肯定有同构且响应式/自适应的解决方案。

我对"variation1"是什么很好奇。我还不确定它是否与你的问题相关,但这个变化只是视图的另一种呈现方式,还是还有其他事情发生了呢? - Peter David Carter
“变体1”和“变体2”共享相同的数据,但视图看起来不同,并且行为也不同。就像1是手风琴式的东西,而2只是静态布局。我认为这与问题无关。上述描述的问题更加普遍。 - Krad
2个回答

6
在我看来,这是一个非常困难的问题,有很多基于“情况而定”的解决方案。 :)
我是react-sizemereact-component-queries的作者,这两个库可以帮助实现响应式组件,并且我遇到了与你在问题中描述的类似的问题。根据我的经验,解决其中一个问题通常会影响另一个问题。下面我将通过描述我的经验来详细说明这一点... 我先尝试解决您的“问题2”: 由于默认状态引起的渲染闪烁是我在创建react-sizeme库时遇到的问题。 react-sizeme是一个高阶组件,它获取可用于您组件的大小,然后将其传递给您的组件。基于大小,您当然可以选择呈现不同的组件,就像您在示例中所做的那样,因此更新闪烁可能会发生,除非您碰巧达到默认状态的甜点。我通过更改react-sizeme来解决这个问题,最初呈现一个空占位符以获取可用的宽度/高度,然后仅呈现您的组件,为其提供“正确”的宽度/高度。这对我非常有效。不再看到ComponentBob被呈现,只需卸载并立即在其位置呈现ComponentFoo。

然后“问题1”出现了...

react-sizeme开始受到欢迎,最终我有一个使用此库的用户希望在服务器端渲染上下文中使用它。但是,由于我为问题1提出的修复方法,服务器端渲染将产生大量空白内容(即我所说的占位符)。在有效载荷传递到浏览器后,占位符逻辑将启动,并最终将大小数据发送到组件并呈现它。这不理想,因为实际上您从一开始就使SSR的任何好处无效。我与这个用户合作,我们决定前进的最佳方法是允许react-sizeme配置为运行在“SSR模式”下。基本上,这意味着删除占位符呈现并允许呈现默认组件,以便您在初始服务器响应时不会获得空白页面,但然后您可以轻松遭受组件闪烁问题!

啊啊啊啊啊!这里影响的升降机! :(

因此,解决一个问题直接影响另一个问题。


我认为最好的方法可能是在第一次请求时尝试获取用户浏览器的宽度/高度。这基本上意味着渲染一个简单的实用程序来获取此信息,然后将其发送回服务器,以渲染用户的初始请求。然后,您可以使用宽度/高度并通过整个组件树(沿途进行数学计算)传递它,以继续确定每个组件的可用高度/宽度。这里非常棘手,但可能有效。
当然,另一个危险是谷歌仅为初始请求(即实用程序的空白渲染,以获取初始宽度/高度)索引空白页面。您必须尝试研究使用一些聪明的HTTP响应代码,例如重定向等,以确保谷歌跟随路径到正确的渲染输出。

抱歉,这可能不是您想要的答案,但希望我的经验能在某种程度上有所帮助或提供一些灵感。如果您有一些有趣的实验,请务必告诉我。我很乐意与您合作。


1
感谢您的回复@ctrlplusb。对我来说,这似乎是一个严重的问题。我在服务器上呈现的原因是为了SEO。因为在“移动”和“桌面”上内容相同,在我的情况下,爬虫读取哪个版本并不重要。您描述的“SSR模式”基本上就是我现在拥有的默认状态,但会出现闪烁。在服务器命中和React呈现之间使用某种中间件的想法听起来有点棘手,而且还意味着额外的请求(降低性能)。如果我找到解决方法,我会在这里通知您。 - Krad
只是提醒一下,我目前没有太多时间来解决上述问题,所以我选择在服务器上使用'mobile-detect.js'并将头信息传递到状态。这样我就不能通过特定的宽度进行自适应,但我的团队对此很满意。 - Krad
我不是SEO专家,但对我来说,闪烁的解决方案并不是一个解决方案。我认为爬虫(现在也能读取JS)看到内容一瞬间就变成另一个内容,他们并不会感到高兴。 - cl0udw4lk3r

0

你可以从服务器的头部获取用户代理,然后使用一个reducer将用户代理信息作为redux action分发,并返回数据。这样,你就可以检测用户是在手机、平板电脑、桌面电脑等设备上,并相应地进行渲染。


我真的觉得仅仅检测设备是不够的了。现在你需要检测分辨率,因为设备本身具有如此多变的分辨率。平板电脑甚至可以与台式机和笔记本电脑相提并论。 - ctrlplusb
@ctrlplusb 是的,我有点同意你的观点,即有许多设备本身具有不同的分辨率,但有时仅使用移动设备、平板电脑和桌面视图可能已经足够,这取决于使用情况。 - alpha
我同意@ctrlplusb的观点。我可以嗅探设备,但我认为这不是一种未来可靠的方式。市面上有太多的设备(未来还会有更多)。另一个要求是在767像素处“断开”。所有<将呈现“变体1”,<“变体2” - Krad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接