用React Router 4构建通用JavaScript应用

简介:

React Router 是一个在 React 领域非常流行的库。它依靠位置栏上的请求 URL 和 浏览器的操作历史来渲染不同的页面内容来保持显示,进而将你的 app 同步到用户接口的页面上。

新的闪闪发亮

最近,版本4的 React Router 已经进入 beta 发布阶段。损誉各半,React Router 的这一次发布是对上一版本的完整重写,这导致了很多破坏性的 API 变更。

在版本 4 的核心理念是 “声明式可组合性(declarative composability)”?—— 它包含 React 之所以优秀的组件概念,并将其应用于 routing。React Router 4 的每一个部分都是 React 的组件: Router ,  Route ,  Link 等等。

React Router 的一位开发者, Ryan Florence ,亲手制作了一个简短的视频来介绍最新的 React Router,这段视频获得了很多人的推荐:

视频: https://youtu.be/a4kqMQorcnE

后台如何?

新版本的 React Router 奉上了一个新的 web 页面,上面有 许多实用的代码示例 ,但没有提供实例介绍如何在服务端使用 React Router 来进行基于 React 的页面的渲染。

对于我近期正在进行的项目,对搜索引擎友好且具备最佳的网站运行速度是重中之重,难道这样就要在客户端渲染整个页面?难道就用示例页面上所有实例所采取的办法?那是不可取的。我们要使用一个 Express 服务器来在后台对 React 页面进行渲染。

在其介绍视屏中, Ryan 有一个可以从某些 API 获取数据来初始化其状态的 App 组件, 使用的是 componentDidMount 生命周期方法。但异步数据的获取操作完毕,组件就会被更新以显示数据。

但是当要在服务端对 App 组件进行渲染的时候这样做不会有效果: 在你使用 renderToString 的时候, 带有 HTML 代码的字符串在调用了组件的渲染方法之后就会被同步地创建出来。 componentDidMount 从未被调用到。

因此如果我们使用 Ryan 视频里的示例在后台渲染出 App 组件,它只会生成一条 “Loading…” 消息。

解决方案

作为对概念方案的验证,我创建了一个 demo 应用,基本上就是对视频中 Ryan 的示例进行重造,但采取的是服务器端的渲染。

应用使用了 GitHub API 去获取有关于 Gist 代码片段的数据:

代码展示

你可以在 Github 上找到 demo 应用的源代码:

https://github.com/technology-ebay-de/universal-react-router4

简而言之,下面就是我所做的事情…

server/index.js

这就是每次有 HTTP 请求发到 Express 服务器的时候都会跑一次的代码:

const routes = [
    '/',
    '/g/:gistId'
];

app.get('*', (req, res) => {
    const match = routes.reduce((acc, route) => matchPath(req.url, route, { exact: true }) || acc, null);
    if (!match) {
        res.status(404).send(render(<NoMatch />));
        return;
    }
    fetch('https://api.github.com/gists')
        .then(r => r.json())
        .then(gists => 
            res.status(200).send(render(
                (
                    <StaticRouter context={{}} location={req.url}>
                        <App gists={gists} />
                    </StaticRouter>
                ), gists
            ))
        ).catch(err => res.status(500).send(render(<Error />));
});

app.listen(3000, () => console.log('Demo app listening on port 3000'));

(注意:这里只是摘录,你可以在  GitHub  找到完整的源代码 )

在第 1–4行 , 我为 app 定义了一个路由数组。数组的第一个元素就是针对主页的初始请求, 没有 Gists 被选上。第二个路由就是用来展示一个被选上的 Gist 的。

在第 6行 , 我的 Express app 被告知要处理任何可以使用星号匹配上的请求。

在第 7行 , 我使用了来自于 React Router 的  matchPath 函数对路由数组进行简省; 结果就是一个匹配对象,其拥有关于能匹配到的路由以及任何可以从 URL 路径转换过来的参数,这些信息。

在第 8–11行 , 如果有不能匹配的路由,我就渲染出一个错误页面,上面会说 : “页面没找到(Page not found)”。

这里的 render 函数只是围绕 React 的  renderToString 的一个封装而已,添加了包围 React 组件的 HTML 的基础页面的 HTML 代码 ( <html><head><body> , 等等)。

在第 12–22行 , 我会从 GitHub API 获取到可以填充 App 状态的数据,并且对 App 组件进行渲染。

最显而易见是第 17 行,我使用了 StaticRouter 组件来初始化 React Router。该 Router 组件类型是采用服务端渲染方案的最佳选择。它永远不会改变位置, 这是我们在本场景下所需要的, 因为是在后台上, 我们只会渲染一次,而且不会直接地对用户的交互操作做出反应。

而第 23 行 会捕获在处理期间产生的错误信息来渲染出一个错误页面。

我的 App 组件看起来像下面这样:

export default ({ gists }) => (
    <div>
        <Sidebar>
            {
                gists ? gists.map(gist => (
                    <SidebarItem key={gist.id}>
                        <Link to={`/g/${gist.id}`}>
                            {gist.description || '[no description]'}
                        </Link>
                    </SidebarItem>
                )) : (<p>Loading…</p>)
            }
        </Sidebar>
        <Main>
            <Route path="/" exact component={Home} />
            {
                gists && (
                    <Route path="/g/:gistId" render={                         ({ match }) => (
                            <Gist gist={gists.find(g => g.id === match.params.gistId)} />
                        )
                    } />
                )
            }
        </Main>
    </div>
);

(→  GitHub上有完整的源代码 )

在第 1 行 , 组件接收到作为一个属性的 Gist 数据对象。

第 3–13行 渲染了一个  Sidebar 组件,里面是连接到不同 Gist 链接。  SidebarItem 组件里面所包含的是只有在存在实际的 Gist 数据时才会被渲染的数据。在服务端,总会有这样的情况发生。而我们在服务端和客户端渲染中都会用到该组件。如果组件是在客户端被渲染的, 我们可能处在获取新的 Gist 数据这一过程之中,所以会展示出一条 “Loading…(加载中)” 的消息。

第 15行 使用了一个来自于 React Router 库的  Route 组件,用以在路由匹配到“/”这个路径的时候展示出  Home 组件。这里我们使用的是精确匹配, 不然的话任何以斜线开头的路径都会匹配到。

如果有 Gist 数据要展示的话, 那么在第 18 行 , 另外一个 Route 组件就会被用来展示一个  Gist 组件,上面是被选择的 Gist 的详细信息。

client/index.js

如前所述,这是一个 通用 JavaScript 应用(大家都知道的“同构”),意思是相同的代码即可用于渲染服务器页面,又可以用于渲染客户端页面。这里摘录了一段在客户端初始化页面的代码:

render((
    <BrowserRouter>
        <App gists={window.__gists__} />
    </BrowserRouter>
), document.getElementById('app'));

(→  GitHub 上的完整代码 )

这比服务端代码简单多了! 第 1 行 的  render 函数就是  ReactDOM 的 render 函数 。它把我的 React 组件渲染出来的布局附加到 DOM 节点上。

第 2 行 使用了  BrowserRouter (代替了我在服务端进行渲染使用的 StaticRouter)。

第 3 行 我使用 gist 数据对 App 组件进行实例化,以此代替通过 GitHub API 获取数据。gist 数据来自浏览器 DOM 的全局变量,它是后端通过 <script> 标签放在那里的。

基本上就这些了!

当我需要在浏览器中打开应用程序时,我可以点击侧边栏中的任何 Gist。客户端的 Reactor 路由确保每次点击链接时,页面的网址都会更新,并且依赖于新网址的网页部分会刷新。 当我点击浏览器的重载按钮时,后端的静态路由确保显示与之对应的数据页面。


作者:无若

来源:51CTO

相关文章
|
29天前
|
监控 JavaScript 算法
如何使用内存监控工具来定位和解决Node.js应用中的性能问题?
总之,利用内存监控工具结合代码分析和业务理解,能够逐步定位和解决 Node.js 应用中的性能问题,提高应用的运行效率和稳定性。需要耐心和细致地进行排查和优化,不断提升应用的性能表现。
181 77
|
1月前
|
JSON 缓存 JavaScript
深入浅出:使用Node.js构建RESTful API
在这个数字时代,API已成为软件开发的基石之一。本文旨在引导初学者通过Node.js和Express框架快速搭建一个功能完备的RESTful API。我们将从零开始,逐步深入,不仅涉及代码编写,还包括设计原则、最佳实践及调试技巧。无论你是初探后端开发,还是希望扩展你的技术栈,这篇文章都将是你的理想指南。
|
27天前
|
存储 缓存 监控
如何使用内存监控工具来优化 Node.js 应用的性能
需要注意的是,不同的内存监控工具可能具有不同的功能和特点,在使用时需要根据具体工具的要求和操作指南进行正确使用和分析。
67 31
|
23天前
|
JSON JavaScript 前端开发
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将引导您步入Node.js的奇妙世界,通过实践操作,掌握如何使用这一强大的JavaScript运行时环境构建高效、可扩展的RESTful API。我们将一同探索Express框架的使用,学习如何设计API端点,处理数据请求,并实现身份验证机制,最终部署我们的成果到云服务器上。无论您是初学者还是有一定基础的开发者,这篇文章都将为您打开一扇通往后端开发深层知识的大门。
38 12
|
28天前
|
JavaScript 前端开发 API
深入理解Node.js事件循环及其在后端开发中的应用
本文旨在揭示Node.js的核心特性之一——事件循环,并探讨其对后端开发实践的深远影响。通过剖析事件循环的工作原理和关键组件,我们不仅能够更好地理解Node.js的非阻塞I/O模型,还能学会如何优化我们的后端应用以提高性能和响应能力。文章将结合实例分析事件循环在处理大量并发请求时的优势,以及如何避免常见的编程陷阱,从而为读者提供从理论到实践的全面指导。
|
27天前
|
JavaScript
如何使用内存快照分析工具来分析Node.js应用的内存问题?
需要注意的是,不同的内存快照分析工具可能具有不同的功能和操作方式,在使用时需要根据具体工具的说明和特点进行灵活运用。
41 3
|
1月前
|
JavaScript 前端开发 安全
JavaScript与TypeScript的对比,分析了两者的特性及在实际项目中的应用选择
本文深入探讨了JavaScript与TypeScript的对比,分析了两者的特性及在实际项目中的应用选择。JavaScript以其灵活性和广泛的生态支持著称,而TypeScript通过引入静态类型系统,提高了代码的可靠性和可维护性,特别适合大型项目。文章还讨论了结合使用两种语言的优势,以及如何根据项目需求和技术背景做出最佳选择。
56 4
|
1月前
|
机器学习/深度学习 人工智能 JavaScript
JavaScript和TypeScript的未来发展趋势及其在Web开发中的应用前景
本文探讨了JavaScript和TypeScript的未来发展趋势及其在Web开发中的应用前景。JavaScript将注重性能优化、跨平台开发、AI融合及WebAssembly整合;TypeScript则强调与框架整合、强类型检查、前端工程化及WebAssembly的深度结合。两者结合发展,特别是在Vue 3.0中完全采用TypeScript编写,预示着未来的Web开发将更加高效、可靠。
42 4
|
1月前
|
JavaScript NoSQL API
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发如同一座灯塔,指引着数据的海洋。本文将带你航行在Node.js的海域,探索如何从一张白纸到完成一个功能完备的RESTful API。我们将一起学习如何搭建开发环境、设计API结构、处理数据请求与响应,以及实现数据库交互。准备好了吗?启航吧!
|
1月前
|
前端开发 JavaScript 关系型数据库
基于 Vue2.0 + Nest.js 全栈开发的后台应用
Vue2 Admin 是一个基于 Vue2 和 Ant Design Pro 开发的前端项目,配合 Nest.js 构建的后端,提供了一个完整的全栈后台应用解决方案。该项目支持动态国际化、用户权限管理、操作日志记录等功能,适合全栈开发者学习参考。线上预览地址:https://vue2.baiwumm.com/,用户名:Admin,密码:abc123456。