目錄
一、前端路由與後端路由
1)後端路由
2)前端路由
二、React Router 安裝
三、路由器
BrowserRouter 建立的URL 形式如下:
HashRouter 建立的URL 形式如下:
四、路由器
1) path
2)match
3)Route 渲染组件的方式
4)Switch 和 exact
5)嵌套路由
五、链接
六、路由设计
1)定义路由名称
2)组织 Route 结构层次
七、代码分片
首頁 web前端 js教程 React Router知識的全面解析(程式碼範例)

React Router知識的全面解析(程式碼範例)

Sep 17, 2018 pm 02:06 PM
react.js

這篇文章帶給大家的內容是關於React Router知識的全面解析(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

一、前端路由與後端路由

1)後端路由

多頁應用程式中,一個URL對應一個HTML頁面,一個Web應用程式包含許多HTML頁面,在多頁應用程式中,頁面路由控制由伺服器端負責,這種路由方式稱為後端路由。

多頁應用程式中,每次頁面切換都需要向伺服器發送一次請求,頁面使用到的靜態資源也需要重新加載,存在一定的浪費。而且,頁面的整體刷新對使用者體驗也有影響,因為不同頁間往往存在共同的部分,例如導覽列、側邊欄等,頁面整體刷新也會導致共用部分的刷新。

2)前端路由

在單面應用程式中,URL發生並不會向伺服器發送新的請求,所以「邏輯頁面」的路由只能由前端負責,這種路由方式稱為前端路由。

目前,國內的搜尋引擎大多對單頁應用的SEO支援的不好,因此,對於SEO 非常看重的Web
應用(例如,企業官方網站,電商網站等),一般還是會選擇採用多頁面應用程式。 React 也並非只能用於開發單頁應用程式。

二、React Router 安裝

這裡使用的 React Router 的大版本號碼是 v4, 這也是目前最新版本。

React Router 包含3個函式庫, react-router、react-router-dom、和 react-router-native。 react-router 提供最基本的路由功能,實際使用,我們不會直接安裝react-router,而是根據應用運行的環境選擇安裝react-router-dom(在瀏覽器中使用)或react-router-native(在react-native中使用)。 react-router-dom 和 react-router-native 都依賴 react-router,所以在安裝時, react-router 也會自動安裝。
建立 Web應用,使用

npm install react-router-dom
登入後複製

建立 navtive 應用,使用

 npm install react-router-native
登入後複製

三、路由器

React Router 透過 Router 和 Route 兩個元件完成路由功能。 Router 可以理解成路由器,一個應用程式中需要一個 Router 實例,所有跌幅設定元件 Route 都定義為 Router 的子元件。在 Web應用程式中,我們一般會使用對 Router 進行包裝的 BrowserRouter 或 HashRouter  兩個元件 BrowserRouter使用 HTML5 的 history API(pushState、replaceState等)來實現應用程式的 UI 和 URL 的同步。 HashRouter 使用 URL 的 hash 實作應用程式的 UI 和 URL 同步。

BrowserRouter 建立的URL 形式如下:

http://example.com/some/path
登入後複製

HashRouter 建立的URL 形式如下:

 http://example.com/#/some/path
登入後複製

使用BrowserRouter 時,一般也需要對伺服器進行配置,讓伺服器能正確地處理所有可能的URL。例如,當瀏覽器發生http://example.com/some/path 和http://example.com/some/path2 兩個請求時,伺服器需要能傳回正確的HTML 頁面(也就是單頁應用程式中唯一的HTML 頁面)

HashRouter 則不存在這個問題,因為hash 部分的內容會被伺服器自動忽略,真正有效的資訊是hash 前端的部分,而對於單頁應用程式來說,這部分是固定的。

Router 會建立一個 history 對象,history 用來追蹤 URL, 當URL 改變時, Router,的後代元件會重新渲染。 React Router 中提供的其他元件可以透過 context 取得 history 對象,這也隱含說明了 React Router 中其他元件必須作為 Router 元件後代使用。但Router 中只能唯一的一個子元素,例如:

// 正确
ReactDOM.render(
  (
  <BrowserRouter>
    <App />
  </BrowserRouter>),
  document.getElementById('root')
)
//错误,Router 中包含两个子元素
ReactDOM.render(
  (
    <BrowserRouter>
      <App1 />
      <App2 />
    </BrowserRouter>),
  document.getElementById('root')
)
登入後複製

四、路由器

#Route 是React Router中用來設定路由資訊的元件,也是React Router 中使用頻率最高的組件。每當有一個元件需要根據 URL 決定是否渲染時,就需要建立一個 Route。

1) path

每個Route 都需要定義一個path 屬性,當使用BrowserRouter 時,path 用來描述這個Router符合的URL 的pathname;使用HashRouter時,path 用來描述這個Route 相符的URL 的hash。例如,使用 BrowserRouter 時, 會符合一個 pathname 以 foo 開始的 URL (如: http://example.com/foo)。當 URL 符合一個 Route 時,這個 Route 中定義的元件就會被渲染出來。

2)match

當 URL 和 Route匹配時,Route 會建立一個 match 物件作為 props 中的一個 屬性傳遞給被渲染的元件。這個物件包含以下4個屬性。

(1)params: Route的 path 可以包含參數,例如

(2)isExact: 是一個布林值,當URL 完全匹時,值為true; 當URL 部分匹配時,值為false.例如,當path='/foo'、URL="http ://example.com/foo" 時,是完全匹配; 當URL="http://example.com/foo/1" 時,是部分匹配。

(3)path: Route 的 path 属性,构建嵌套路由时会使用到。

(4)url: URL 的匹配的方式

3)Route 渲染组件的方式

(1)component

component 的值是一个组件,当 URL 和 Route 匹配时,Component属性定义的组件就会被渲染。例如:

<Route path=&#39;/foo&#39; component={Foo} ></p>
<p>当 URL = "http://example.com/foo" 时,Foo组件会被渲染。</p>
<p>(2) render<br>render 的值是一个函数,这个函数返回一个 React 元素。这种方式方便地为待渲染的组件传递额外的属性。例如:</p>
<pre class="brush:php;toolbar:false"><Route path=&#39;/foo&#39; render={(props) => {
  <Foo {...props} data={extraProps} />
}}>
</Route>
登入後複製

Foo 组件接收了一个额外的 data 属性。

(3)children
children 的值也是一个函数,函数返回要渲染的 React 元素。 与前两种方式不同之处是,无论是否匹配成功, children 返回的组件都会被渲染。但是,当匹配不成功时,match 属性为 null。例如:

<Route path=&#39;/foo&#39; render={(props) => {
  <p className={props.match ? &#39;active&#39;: &#39;&#39;}>
    <Foo {...props} data={extraProps} />
  </p>
}}>
</Route>
登入後複製

如果 Route 匹配当前 URL,待渲染元素的根节点 p 的 class 将设置成 active.

4)Switch 和 exact

当URL 和多个 Route 匹配时,这些 Route 都会执行渲染操作。如果只想让第一个匹配的 Route 沉浸,那么可以把这些 Route 包到一个 Switch 组件中。如果想让 URL 和 Route 完全匹配时,Route才渲染,那么可以使用 Route 的 exact 属性。Switch 和 exact 常常联合使用,用于应用首页的导航。例如:

<Router>
 <Switch>
    <Route exact path=&#39;/&#39; component={Home}/>
    <Route exact path=&#39;/posts&#39; component={Posts} />
    <Route exact path=&#39;/:user&#39; component={User} />
  </Switch>
</Router>
登入後複製

如果不使用 Switch,当 URL 的 pathname 为 "/posts" 时, 都会被匹配,但显然我们并不希望 被匹配,实际上也没有用户名为 posts 的用户。如果不使用 exact, "/" "/posts" "/user1"等几乎所有 URL 都会匹配第一个 Route,又因为Switch 的存在,后面的两个 Route永远不会被匹配。使用 exact,保证 只有当 URL 的 pathname 为 '/'时,第一个Route才会匹配。

5)嵌套路由

嵌套路由是指在Route 渲染的组件内部定义新的 Route。例如,在上一个例子中,在 Posts 组件内再定义两个 Route:

const Posts = ({match}) => {
  return (
    <p>
      {/* 这里 match.url 等于 /posts */}
      <Route path={`${match.url}/:id`} component={PostDetail} />
      <Route exact path={match.url} component={PostList} />
    </p>
  )
}
登入後複製

五、链接

Link 是 React Router提供的链接组件,一个 Link 组件定义了当点击该 Link 时,页面应该如何路由。例如:

const Navigation = () => {
  <header>
    <nav>
      <ul>
        <li><Link to=&#39;/&#39;>Home</Link></li>
        <li><Link to=&#39;/posts&#39;>Posts</Link></li>
      </ul>
    </nav>
  </header>
}
登入後複製

Link 使用 to 属性声明要导航到的URL地址。to 可以是 string 或 object 类型,当 to 为 object 类型时,可以包含 pathname、search、hash、state 四个属性,例如:

<Link to={{
  pathname: &#39;/posts&#39;,
  search: &#39;?sort=name&#39;,
  hash:&#39;#the-hash&#39;,
  state: { fromHome: true}
}}>
</Link>
登入後複製

除了使用Link外,我们还可以使用 history 对象手动实现导航。history 中最常用的两个方法是 push(path,[state]) 和 replace(path,[state]),push会向浏览器记录中新增一条记录,replace 会用新记录替换记录。例如:

history.push('/posts');
history.replace('/posts');
登入後複製

六、路由设计

路由设计的过程可以分为两步:

  1. 为每一个页面定义有语义的路由名称(path)

  2. 组织 Route 结构层次

1)定义路由名称

我们有三个页面,按照页面功能不难定义出如下的路由名称:

  • 登录页: /login

  • 帖子列表页: /posts

  • 帖子详情页: /posts/:id(id代表帖子的ID)

但是这些还不够,还需要考虑打开应用时的默认页面,也就是根路径"/"对应的页面。结合业务场景,帖子列表作为应用的默认页面为合适,因此,帖子列表对应两个路由名称: '/posts'和 '/'

2)组织 Route 结构层次

React Router 4并不需要在一个地方集中声明应用需要的所有 Route, Route实际上也是一个普通的 React 组件,可以在任意地方使用它(前提是,Route必须是 Router 的子节点)。当然,这样的灵活性也一定程度上增加了组织 Route 结构层次的难度。
我们先考虑第一层级的路由。登录页和帖子列表页(首页)应该属于第一层级:

<Router>
  <Switch>
    <Route exact path="/" component={Home}></Route>
    <Route exact path="/login" component={Login}></Route>
    <Route exact path="/posts" component={Home}></Route>
  </Switch>
</Router>
登入後複製

第一个Route 使用了 exact 属性,保证只有当访问根路径时,第一个 Route 才会匹配成功。Home 是首页对应组件,可以通过 "/posts" 和 “/” 两个路径访问首页。注意,这里并没有直接渲染帖子列表组件,真正渲染帖子列表组件的地方在 Home 组件内,通过第二层级的路由处理帖子列表组件和帖子详情组件渲染,components/Home.js 的主要代码如下:

class Home extends Component {
  /**省略其余代码 */
  render() {
    const {match, location } = this.props;
    const { username } = this.state;
    return(
      <p>
        <Header
          username = {username}
          onLogout={this.handleLogout}
          location = {location}
        >
        </Header>
        {/* 帖子列表路由配置 */}
        <Route
          path = {match.url}
          exact
          render={props => <PostList username={username} {...this.props}></PostList>}
        ></Route>
      </p>
    )
  }
}
登入後複製

Home的render内定义了两个 Route,分别用于渲染帖子列表和帖子详情。PostList 是帖子列表组件,Post是帖子详情组件,代码使用Router 的render属性渲染这两个组件,因为它们需要接收额外的 username 属性。另外,无论访问是帖子列表页面还是帖子详情页面,都会共用相同 Header 组件。

七、代码分片

默认情况下,当在项目根路径下执行 npm run build 时 ,create-react-app内部使用 webpack将 src路径下的所有代码打包成一个 JS 文件和一个 Css 文件。

当项目代码量不多时,把所有代码打包到一个文件的做法并不会有什么影响。但是,对于一个大型应用,如果还把所有的代码都打包到一个文件中,显然就不合适了。

create-react-app 支持通过动态 import() 的方式实现代码分片。import()接收一个模块的路径作为参数,然后返回一个 Promise 对象, Promise 对象的值就是待导入的模块对象。例如

// moduleA.js

const moduleA = 'Hello'
export { moduleA };

// App.js

import React, { Component } from 'react';

class App extends Component {
  handleClick = () => {
    // 使用import 动态导入 moduleA.js
    import('./moduleA')
      .then(({moduleA}) => {
        // 使用moduleA
      })
      .catch(err=> {
        //处理错误
      })
  };
  render() {
    return(
      <p>
        <button onClick={this.handleClick}>加载 moduleA</button>
      </p>
    )
  }
}

export default App;
登入後複製

上面代码会将 moduleA.js 和它所有依赖的其他模块单独打包到一个chunk文件中,只有当用户点击加载按钮,才开始加载这个 chunk 文件。
当项目中使用 React Router 是,一般会根据路由信息将项目代码分片,每个路由依赖的代码单独打包成一个chunk文件。我们创建一个函数统一处理这个逻辑:

import React, { Component } from 'react';
// importComponent 是使用 import()的函数
export default function asyncComponent(importComponent) {
  class AsyncComponent extends Component {
    constructor(props) {
      super(props);
      this.state = {
        component:  null //动态加载的组件
      }
    }
    componentDidMount() {
      importComponent().then((mod) => {
        this.setState({
          // 同时兼容 ES6 和 CommonJS 的模块
          component: mod.default ? mod.default : mod;
        });
      })
    }
    render() {
      // 渲染动态加载组件
      const C = this.state.component;
      return C ? <C {...this.props}></C> : null
    }
  }

  return AsyncComponent;
}
登入後複製

asyncComponent接收一个函数参数 importComponent,  importComponent 内通过import()语法动态导入模块。在AsyncComponent被挂载后,importComponent就会阴调用,进而触发动态导入模块的动作。
下面利用 asyncComponent 对上面的例子进行改造,代码如下:

import React, { Component } from 'react';
import { ReactDOM, BrowserRouter as Router, Switch, Route } from 'react-dom';
import asyncComponent from './asyncComponent'
//通过asyncComponent 导入组件,创建代码分片点
const AsyncHome = asyncComponent(() => import("./components/Home"))
const AsyncLogin = asyncComponent(() => import("./components/Login"))

class App extends component {
  render() {
    return(
      <Router>
        <Switch>
          <Route exact path="/" component={AsyncHome}></Route>
          <Route exact path="/login" component={AsyncLogin}></Route>
          <Route exact path="/posts" component={AsyncHome}></Route>
        </Switch>
      </Router>
    )
  }
}

export default App;
登入後複製

这样,只有当路由匹配时,对应的组件才会被导入,实现按需加载的效果。

这里还有一个需要注意的地方,打包后没有单独的CSS文件了。这是因为 CSS样子被打包到各个 chunk 文件中,当 chunk文件被加载执行时,会有动态把 CSS 样式插入页面中。如果希望把 chunk 中的 css打包到一个单独的文件中,就需要修改 webpack 使用的 ExtractTextPlugin 插件的配置,但 create-react-app 并没有直接把 webpack 的配置文件暴露给用户,为了修改相应配置
,需要将 create-react-app 管理的配置文件“弹射”出来,在项目根路径下执行:

npm run eject
登入後複製

项目中会多出两个文件夹:config和 scripts,scrips中包含项目启动、编译和测试的脚本,config 中包含项目使用的配置文件,
webpack配置文件 就在这个路径下,打包 webpack.config.prod.js 找到配置 ExtractTextPlugin 的地方,添加 allChunks:true 这项配置:

new ExtractTextPlugin({
  filename: cssFilename,
  allChunks: true
})
登入後複製

然后重新编译项目,各个chunk 文件 使用的 CSS 样式 又会统一打包到 main.css 中。

以上是React Router知識的全面解析(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1670
14
CakePHP 教程
1428
52
Laravel 教程
1329
25
PHP教程
1273
29
C# 教程
1256
24
React父元件怎麼呼叫子元件的方法 React父元件怎麼呼叫子元件的方法 Dec 27, 2022 pm 07:01 PM

呼叫方法:1、類別元件中的呼叫可以利用React.createRef()、ref的函數式宣告或props自訂onRef屬性來實作;2、函式元件、Hook元件中的呼叫可以利用useImperativeHandle或forwardRef拋出子組件ref來實作。

深入理解React的自訂Hook 深入理解React的自訂Hook Apr 20, 2023 pm 06:22 PM

React 自訂 Hook 是將元件邏輯封裝在可重複使用函數中的方式,它們提供了一種在不編寫類別的情況下重複使用狀態邏輯的方式。本文將詳細介紹如何自訂封裝 hook。

react怎麼設定div高度 react怎麼設定div高度 Jan 06, 2023 am 10:19 AM

react設定div高度的方法:1、透過css方式實現div高度;2、在state中宣告一個物件C,並在該物件中存放更換按鈕的樣式,然後取得A並重新設定C中的「marginTop」即可。

怎麼調試R​​eact源碼?多種工具下的除錯方法介紹 怎麼調試R​​eact源碼?多種工具下的除錯方法介紹 Mar 31, 2023 pm 06:54 PM

怎麼調試R​​eact源碼?以下這篇文章帶大家聊聊多種工具下的調試React源碼的方法,介紹一下在貢獻者、create-react-app、vite專案中如何debugger React的真實源碼,希望對大家有所幫助!

React為什麼不將Vite作為構建應用的首選 React為什麼不將Vite作為構建應用的首選 Feb 03, 2023 pm 06:41 PM

React為什麼不將Vite作為建置應用的首選?以下這篇文章就來帶大家聊聊React不將Vite當作預設推薦的原因,希望對大家有幫助!

7 個很棒且實用的React 元件庫(壓箱底分享) 7 個很棒且實用的React 元件庫(壓箱底分享) Nov 04, 2022 pm 08:00 PM

這篇文章跟大家整理分享7 個很棒又實用的React 元件庫,日常開發中常會用到的,快來收藏試試看吧!

10 個編寫更簡潔React程式碼的實用小技巧 10 個編寫更簡潔React程式碼的實用小技巧 Jan 03, 2023 pm 08:18 PM

這篇文章為大家整理分享 10 個寫更簡潔 React 程式碼的實用小技巧,希望對大家有幫助!

聊聊Vuex與Pinia在設計與實作上的差別 聊聊Vuex與Pinia在設計與實作上的差別 Dec 07, 2022 pm 06:24 PM

在進行前端專案開發時,狀態管理始終是一個繞不開的話題,Vue 與 React 框架本身提供了一部分能力去解決這個問題。但在開發大型應用程式時往往有其他考慮,例如需要更規範更完善的操作日誌、整合在開發者工具中的時間旅行能力、服務端渲染等。本文以 Vue 架構為例,介紹 Vuex 與 Pinia 這兩種狀態管理工具在設計與實作上的差異。

See all articles