购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.1 使用响应式路由创建界面

问题

大多数的应用程序都是在移动设备端和笔记本电脑上使用的,这意味着,你可能希望React应用在大小不同的屏幕下都能很好地工作。要想让你的React应用变成响应式,除了简单地使用CSS修改来调整文本大小和页面布局外,还需要更加实质性的调整,这样才能给浏览你网站移动端和桌面端的用户带来迥然不同的体验。

我们的示例应用程序显示了一组人员的姓名和地址。在图2-1中,你可以看到应用程序在桌面端运行的视图。

图2-1:应用程序的桌面端视图

但这种布局在移动设备上效果不太好,因为移动设备的空间只够显示人名列表或单个人员的详细信息,而无法同时显示。

如果我们不想创建两个完全独立的应用程序版本,那么该如何用React为移动端和桌面端用户提供自定义导航体验呢?

解决方案

我们将使用响应式路由(responsive route)。响应式路由根据用户显示屏的大小而变化。我们现有的应用程序使用单一路由来显示个人的信息: /people/:id [1]

切换到该路由后,浏览器显示如图2-1所示的界面。你可以看到人员列表显示在左侧。该页面高亮显示选中的人,并在右侧显示他们的详细信息。

我们将修改应用程序来处理另一个路由 /people 。然后,我们将使路由响应式化,以便用户在不同的设备上看到不同的内容:

要实现这个功能,我们需要什么工具呢?首先,要安装react-router-dom,如果还没有安装的话,使用如下命令安装:

react-router-dom库让我们可以协调浏览器的当前地址与应用程序的状态。接下来,我们将安装react-media库,它允许我们创建React组件来响应屏幕大小的变化:

现在,我们将创建一个响应式PeopleContainer组件,它将管理我们要创建的路由。在小屏幕上,该组件将显示人员列表或单个人员的详细信息。在大屏幕上,它会显示一个组合视图:左侧显示人员列表,右侧显示单个人员的详细信息。

PeopleContainer将使用来自react-media的Media组件。Media组件的使用规则与CSS的@media类似:允许你指定在特定屏幕尺寸范围内显示的具体内容。Media组件接受一个queries属性,用来指定一组屏幕尺寸。我们将定义一个屏幕尺寸——small——将其作为一个区分移动端屏幕和桌面端屏幕的临界点:

Media组件接受一个函数类型的子组件。该函数会被传入一个size对象,用来告知当前屏幕的大小。在我们的例子中,size对象将有一个small属性,我们可以用它来决定要显示哪些其他组件:

在我们讨论返回大屏幕或小屏幕组件的具体代码细节之前,有必要先看看我们将如何在应用程序中挂载PeopleContainer。我们的根组件App的代码如下:

上述代码中,我们使用了react-router-dom的BrowserRouter组件,其内部可以调用浏览器HTML5 History API。我们需要将所有的Route组件都包裹在Router中,以便Route组件能获取到浏览器的当前地址。

在BrowserRouter组件中,包含一个Switch组件。Switch会检索其包含的组件,找到与当前地址匹配的Route组件。在此处的例子中,我们有一个单一Route,能匹配以 /people 开头的路径。匹配成功后,会渲染PeopleContainer组件。如果没有匹配,那么会显示Switch里末尾的一个Link,链接到 /people 。因此,当用户进入应用程序的首页时,他们只能看到一个到People页面的链接。

代码将匹配以指定path的属性值作为开头的路由,而如果设置了exact属性,那么只有在整个路径都匹配时才会展示这个路由。

如果浏览器渲染了PeopleContainer组件,说明我们正在访问的路由是以 /people/... 开头的。如果我们使用的是小屏幕,那么浏览器要么显示人员类别,要么显示单个人员的详细信息,而不会两者同时显示。对此,我们可以用Switch组件来实现:

在小屏幕设备上,Media组件将调用size.small的值是true的子分支。如果当前路径包含id,这里的Switch将会展示Person组件。否则,Switch将无法匹配Route,而是呈现一个PeopleList。

暂不考虑为大屏幕编写代码,如果现在在移动设备上运行这段代码并单击首页上的People链接,那么我们将导航到人员列表,应用程序会渲染PeopleList组件。PeopleList组件展示了一组指向人员列表的链接,其路径形式为 /people/id [2] 。当用户从左侧列表单击一个人员时,右侧组件会被重新渲染,此时PeopleContainer显示单个人员的详细信息(如图2-2所示)。

图2-2:移动端视图:人员列表(左)链接到单个人员的详细信息(右)

到目前为止,效果看起来还不错。现在,我们需要确保我们的应用程序在更大的屏幕上仍然可以工作。当size.small为false时,我们需要在PeopleContainer中应用响应式路由。如果当前路径的形式是 /people/id ,我们可以在左边显示PeopleList组件,在右边显示Person组件:

遗憾的是,这不能处理当前路径是 /people 的情况。我们需要另一个Switch,它可以显示单个人员的详细信息,也可以重定向到 /people/first-person-id ,定位到人员列表中的第一个人。

Redirect组件并不执行实际的浏览器重定向。它只是把当前路径更新为 /people/first-person-id ,这将导致PeopleContainer重新渲染。这类似于在JavaScript中调用history.push(),只是它没有向浏览器历史记录中添加额外的记录。如果用户导航到 /people ,浏览器会简单地将当前路径更新为 /people/first-person-id

如果现在在笔记本电脑或更大的平板电脑上访问 /people ,我们会看到人员列表,旁边展示第一个人的详细信息(如图2-3所示)。

图2-3: http://localhost:3000/people 在大屏幕上的展示效果

下面是PeopleContainer的最终版本:

讨论

当你第一次见到组件内部的声明式路由时,可能会觉得它很奇怪。假设你以前使用过集中式路由模型,那么声明式路由一开始可能看起来很混乱,因为它们将应用程序不同路由的组成部件分散到了多个组件中,而不是集中在一个文件里。你创建的组件不再是和外部世界毫无联系,而是包含了应用程序中使用的路径的详细信息,这可能会降低它们的可移植性。

然而,路由的响应式化展示了声明式路由的真正威力。如果你担心组件和应用程序的路由配置过于耦合,可以考虑将路径字符串提取到单独的文件中管理起来。通过这种方式,组件会根据当前路径和集中的路由配置来决定自己的行为。

你可以从GitHub网站( https://oreil.ly/tZzMD )下载本解决方案的源代码。 2zoY/DTJqfA5u5MXNxvGAzq8nGb0OgcQ7ePo6kADyI3VMrVTxjGJrrTiDyE2pZmf

点击中间区域
呼出菜单
上一章
目录
下一章
×