您可能不需要React Router

汪才
2023-12-01

by Konstantin Tarkus

康斯坦丁·塔库斯(Konstantin Tarkus)

您可能不需要React Router (You might not need React Router)

If you happened to work with Facebook’s React.js library for a while, you might notice a few misconceptions floating in the React community. One of them is the affirmation that React is just V from MVC architecture and needs to be mixed with a bunch of other libraries before it can be used as a framework for developing web applications.

如果您碰巧使用了Facebook的React.js库一段时间,您可能会注意到React社区中存在一些误解。 其中之一是,确认React只是MVC架构中的V,并且需要将其与其他库混合使用,然后才能用作开发Web应用程序的框架。

In practice, you rarely see a React developer using controllers and models from MVC. Component-based UI architecture is steadily taking over in the front-end community and fewer and fewer people are using MVC pattern nowadays.

实际上,您很少看到React开发人员使用MVC中的控制器和模型。 基于组件的UI体系结构在前端社区中正在稳步接管,如今使用MVC模式的人数越来越少。

Another misconception is that React Router library (RR) is the official routing solution from Facebook. In reality, the majority of projects at Facebook don’t even use it.

另一个误解是React Router库(RR)是Facebook的官方路由解决方案。 实际上,Facebook上的大多数项目甚至都没有使用它。

Speaking of the routing, the great multitude of web application projects and use cases can do pretty well with a tiny custom router. Before you classify this notion as a complete heresy, please let me show you how to implement a fully featured routing solution with under 50 lines of code.

说到路由,大量的Web应用程序项目和用例可以用一个很小的自定义路由器很好地完成。 在将此概念归类为完整的异端之前,请让我向您展示如何使用少于50行代码来实现功能全面的路由解决方案。

First of all, it not necessary to combine routing and client-side navigation within the same component as it’s done in RR. This way your router can be truly universal — work in exactly the same way, with the same API in both client- and server-side environments. There is a great npm module called history that can handle the navigation part (FYI, it’s sort of a wrapper for HTML5 History API and is also used internally by RR). You just create history.js file in your project where you initialize this component (class) and use it as a singleton in your app:

首先,没有必要像在RR中一样在同一组件中结合路由和客户端导航。 这样,您的路由器可以真正实现通用性-在客户端和服务器端环境中以完全相同的方式工作,并使用相同的API。 有一个很棒的npm模块,称为历史记录 ,可以处理导航部分(FYI,它有点像HTML5 History API的包装器,并且也由RR内部使用)。 您只需在项目中创建history.js文件,即可在其中初始化此组件(类)并将其作为单例应用程序使用:

import createHistory from 'history/lib/createBrowserHistory';import useQueries from 'history/lib/useQueries';
export default useQueries(createHistory)();

From now on, you can just reference this file and call history.push(‘/new-page’) whenever you need to redirect a user to a new location (URL) without refreshing the whole page. In the main application file (bootstrap code) you can subscribe to all the URL changes as follows:

从现在开始,只要需要在不刷新整个页面的情况下将用户重定向到新位置(URL),您就可以引用此文件并调用history.push('/ new-page')。 在主应用程序文件(引导代码)中,您可以预订所有URL更改,如下所示:

import history from './history';
function render(location) { /* Render React app, read on */ }
render(history.getCurrentLocation()); // render the current URLhistory.listen(render);               // render subsequent URLs

A React component with links working client-side may look like this:

一个具有在客户端工作的链接的React组件可能看起来像这样:

import React from 'react';import history from '../history';
class App extends React.Component {
transition = event => {    event.preventDefault();    history.push({      pathname: event.currentTarget.pathname,      search: event.currentTarget.search    });  };
render() {    return (      <ul>        <li><a href="/" onClick={this.transition}>Home</a></li>        <li><a href="/one" onClick={this.transition}>One</a></li>        <li><a href="/two" onClick={this.transition}>Two</a></li>      </ul>    );  }
}

Though, in practice you may want to extract this “transition” functionality into a stand-alone React component. See Link component in React Static Boilerplate (RSB). So you could write client-side only links like this: <Link to=”/some-page”>Click</Link>.

但是,实际上,您可能希望将此“转换”功能提取到独立的React组件中。 请参阅“ React静态样板” (RSB)中的“ 链接”组件。 因此,您可以这样编写客户端专用链接:<Link to =” / some-page”>单击</ Link>。

Need to display a confirmation message before user leaves a page? Just register history.listenBefore(..) event handler in your component’s componentDidMount() method as described in history module’s docs. The same approach can be used to animate transitions between pages (demo).

是否需要在用户离开页面之前显示确认消息? 只需按照历史记录模块的docs中所述,在您组件的componentDidMount()方法中注册history.listenBefore(..)事件处理程序。 可以使用相同的方法来动画化页面之间的过渡( demo )。

路由 (Routing)

You can describe the list of routes and each route in particular via plain JavaScript objects, no need to use JSX here. For example:

您可以通过简单JavaScript对象来描述路由列表以及每个路由,而无需在此处使用JSX。 例如:

const routes = [  { path: '/', action: () => <HomePage /> },  { path: '/tasks', action: () => <TaskList /> },  { path: '/tasks/:id', action: () => <TaskDetails /> }];

By the way, if someone knows why so many folks prefer using JSX for something not related to UI rendering, please leave a comment.

顺便说一句,如果有人知道为什么这么多人喜欢使用JSX进行与UI渲染无关的事情,请发表评论。

You can write your route handlers by using ES2015+ async/await syntax, there is no need to use callbacks as it’s done in RR. For example:

您可以使用ES2015 + async / await语法编写路由处理程序,而无需像在RR中那样使用回调。 例如:

{  path: '/tasks/:id(\\d+)',  async action({ params }) {    const resp = await fetch(`/api/tasks/${params.id}`);    const data = await resp.json();    return data && <TaskDetails {...data} />;  }}

In the majority of use cases I’m familiar with, there is no need in using nested routes as it’s done in RR. Using nested routes makes things more complicated than they should be and leads to an overly complex hariy routing implementation that is harder to maintain. As far as I know, even at Facebook they don’t use nested routes on the client given the scale of their apps (at least not in all of their projects).

在我所熟悉的大多数用例中,都不需要像在RR中那样使用嵌套路由。 使用嵌套的路由会使事情比应该的复杂得多,并导致难以维护的hariy路由实现过于复杂。 据我所知,即使在Facebook上,鉴于其应用程序的规模(至少不是在所有项目中也是如此),它们也不在客户端上使用嵌套路由。

Instead of nesting routes, you can nest React components, for example:

除了嵌套路线,您还可以嵌套React组件,例如:

import React from 'react';import Layout from '../components/Layout';
class AboutPage extends React.Component {  render() {    return (      <Layout title="About Us" breadcrumbs="Home > About">        <h1>Welcome!</h1>        <p>Here your can learn more about our product.</p>      </Layout>    );  }}
export default AboutPage;

This approach is way simpler in implementation than nested routes and at the same time more flexible, intuitive and unlocks more use cases (notice how you can pass a breadcrumbs component into the Layout).

与嵌套路由相比,此方法的实现方式更简单,同时更灵活,直观,并解锁更多用例(请注意如何将面包屑组件传递给Layout)。

The router itself can be written as a pair of two functions — matchURI(), an internal (private) function that helps to compare a parametrized path string with the actual URL; and resolve() function that traverses the list of routes, finds the route that matches to the given location, executes route handler function and returns result to the caller. Here is how it may look like (router.js):

路由器本身可以写成两个函数对:matchURI(),一个内部(私有)函数,用于将参数化的路径字符串与实际URL进行比较。 和resolve()函数遍历路由列表,找到与给定位置匹配的路由,执行路由处理程序函数并将结果返回给调用方。 这是(router.js)的样子:

import toRegex from 'path-to-regexp';
function matchURI(path, uri) {  const keys = [];  const pattern = toRegex(path, keys); // TODO: Use caching  const match = pattern.exec(uri);  if (!match) return null;  const params = Object.create(null);  for (let i = 1; i < match.length; i++) {    params[keys[i - 1].name] =      match[i] !== undefined ? match[i] : undefined;  }  return params;}
async function resolve(routes, context) {  for (const route of routes) {    const uri = context.error ? '/error' : context.pathname;    const params = matchURI(route.path, uri);    if (!params) continue;    const result = await route.action({ ...context, params });    if (result) return result;  }  const error = new Error('Not found');  error.status = 404;  throw error;}
export default { resolve };

Check out the documentation to the path-to-regexp library. This library is awesome! For example you can use the same library to convert parametrized path strings into URLs:

查阅正则表达式路径库的文档。 这个图书馆很棒! 例如,您可以使用相同的库将参数化的路径字符串转换为URL:

const toUrlPath = pathToRegexp.compile('/tasks/:id(\\d+)')toUrlPath({ id: 123 }) //=> "/user/123"toUrlPath({ id: 'abc' }) /=> error, doesn't match the \d+ constraint

Now you can update the main application file (entry point) to use this router:

现在,您可以更新主应用程序文件(入口点)以使用此路由器:

import ReactDOM from 'react-dom';import history from './history';import router from './router';import routes from './routes';
const container = document.getElementById('root');
function renderComponent(component) {  ReactDOM.render(component, container);}
function render(location) {  router.resolve(routes, location)    .then(renderComponent)    .catch(error => router.resolve(routes, { ...location, error })    .then(renderComponent));}
render(history.getCurrentLocation()); // render the current URLhistory.listen(render);               // render subsequent URLs

That’s it! You may also want to check out my React boilerplate projects that are featuring this routing approach:

而已! 您可能还需要检查一下具有这种路由方法的我的React样板项目:

Universal Router — a simple middleware style routing solutionReact Starter Kit — isomorphic web app boilerplate (Node.js, GraphQL, React)React Static Boilerplate — serverless web app (React, Redux, Firebase)ASP.NET Core Starter Kit — single-page app (ASP.NET Core, C#, React)

Universal Router —一个简单的中间件样式路由解决方案React Starter Kit —同构Web应用程序样板(Node.js,GraphQL,React) React Static Boilerplate —无服务器Web应用程序(React,Redux,Firebase) ASP.NET Core Starter Kit —单页应用(ASP.NET Core,C#,React)

These boilerplates are quite popular and successfully used in many real-world projects around the globe. Definitely worth checking out :)

这些样板非常流行,并已成功用于全球许多实际项目中。 绝对值得一试:)

P.S.: Fan of declarative routes? Find a declarative flavor of this routing approach &gt; here <. Check out comments to this article on &gt; Reddit <.

PS :声明性路线的粉丝? 找到这种路由方法的声明性风味&g t; 在这里 <。 在&g t; 查看对本文的评论。 Reddit <。

Next: You might not need React Router — Part 2 (coming soon)

下一页 :您可能不需要React Router —第2部分(即将推出)

翻译自: https://www.freecodecamp.org/news/you-might-not-need-react-router-38673620f3d/

 类似资料: