亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

React Router V4使用指南(精講)

2024-05-06 16:46:08
字體:
來源:轉載
供稿:網友

一、前端路由和后端路由

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 時,<Route path=''foo' /> 會匹配一個 pathname 以 foo 開始的 URL (如: http://example.com/foo)。當 URL 匹配一個 Route 時,這個 Route 中定義的組件就會被渲染出來。

2)match

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

(1)params: Route的 path 可以包含參數,例如 <Route path="/foo/:id" 包含一個參數 id。params就是用于從匹配的 URL 中解析出 path 中的參數,例如,當 URL = 'http://example.ocm/foo/1' 時,params= {id: 1}。

(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='/foo' component={Foo} >

當 URL = "http://example.com/foo" 時,Foo組件會被渲染。

(2) render

render 的值是一個函數,這個函數返回一個 React 元素。這種方式方便地為待渲染的組件傳遞額外的屬性。例如:

<Route path='/foo' render={(props) => { <Foo {...props} data={extraProps} />}}></Route>

Foo 組件接收了一個額外的 data 屬性。

(3)children

children 的值也是一個函數,函數返回要渲染的 React 元素。 與前兩種方式不同之處是,無論是否匹配成功, children 返回的組件都會被渲染。但是,當匹配不成功時,match 屬性為 null。例如:

<Route path='/foo' render={(props) => { <div className={props.match ? 'active': ''}>  <Foo {...props} data={extraProps} /> </div>}}></Route> 

  如果 Route 匹配當前 URL,待渲染元素的根節點 div 的 class 將設置成 active.

4)Switch 和 exact

當URL 和多個 Route 匹配時,這些 Route 都會執行渲染操作。如果只想讓第一個匹配的 Route 沉浸,那么可以把這些 Route 包到一個 Switch 組件中。如果想讓 URL 和 Route 完全匹配時,Route才渲染,那么可以使用 Route 的 exact 屬性。Switch 和 exact 常常聯合使用,用于應用首頁的導航。例如:

<Router> <Switch>  <Route exact path='/' component={Home}/>  <Route exact path='/posts' component={Posts} />  <Route exact path='/:user' component={User} /> </Switch></Router>

如果不使用 Switch,當 URL 的 pathname 為 "/posts" 時,<Route path='/posts' /> 和 <Route path=':user' /> 都會被匹配,但顯然我們并不希望 <Route path=':user' /> 被匹配,實際上也沒有用戶名為 posts 的用戶。如果不使用 exact, "/" "/posts" "/user1"等幾乎所有 URL 都會匹配第一個 Route,又因為Switch 的存在,后面的兩個 Route永遠不會被匹配。使用 exact,保證 只有當 URL 的 pathname 為 '/'時,第一個Route才會匹配。

5)嵌套路由

嵌套路由是指在Route 渲染的組件內部定義新的 Route。例如,在上一個例子中,在 Posts 組件內再定義兩個 Route:

const Posts = ({match}) => { return (  <div>   {/* 這里 match.url 等于 /posts */}   <Route path={`${match.url}/:id`} component={PostDetail} />   <Route exact path={match.url} component={PostList} />  </div> )}

五、鏈接

Link 是 React Router提供的鏈接組件,一個 Link 組件定義了當點擊該 Link 時,頁面應該如何路由。例如:

const Navigation = () => { <header>  <nav>   <ul>    <li><Link to='/'>Home</Link></li>    <li><Link to='/posts'>Posts</Link></li>   </ul>  </nav> </header>}  

Link 使用 to 屬性聲明要導航到的URL地址。to 可以是 string 或 object 類型,當 to 為 object 類型時,可以包含 pathname、search、hash、state 四個屬性,例如:

<Link to={{ pathname: '/posts', search: '?sort=name', hash:'#the-hash', 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(   <div>    <Header     username = {username}     onLogout={this.handleLogout}     location = {location}    >    </Header>    {/* 帖子列表路由配置 */}    <Route     path = {match.url}     exact     render={props => <PostList username={username} {...this.props}></PostList>}    ></Route>   </div>  ) }}

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.jsconst moduleA = 'Hello'export { moduleA };// App.jsimport React, { Component } from 'react';class App extends Component { handleClick = () => {  // 使用import 動態導入 moduleA.js  import('./moduleA')   .then(({moduleA}) => {    // 使用moduleA   })   .catch(err=> {    //處理錯誤   }) }; render() {  return(   <div>    <button onClick={this.handleClick}>加載 moduleA</button>   </div>  ) }}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 中。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产欧美一区二区三区视频| 91精品国产91久久久久久久久| 国产精品视频自在线| 亚洲激情在线视频| 国产精品白丝jk喷水视频一区| 亚洲国模精品私拍| 国产三级精品网站| 欧美另类69精品久久久久9999| 粗暴蹂躏中文一区二区三区| 亚洲第一福利网站| 久久男人资源视频| 欧美精品久久久久久久久| 国产成人涩涩涩视频在线观看| 色综合天天狠天天透天天伊人| 国产情人节一区| 菠萝蜜影院一区二区免费| 国产ts人妖一区二区三区| 中文字幕久久久av一区| 国产综合福利在线| 亚洲高清一区二| 日韩电影中文字幕一区| 欧美在线免费观看| 欧美国产高跟鞋裸体秀xxxhd| 国语自产精品视频在线看一大j8| 久久精品久久久久久国产 免费| 日本一区二区三区四区视频| 亚洲成人精品久久久| 亚洲美女免费精品视频在线观看| 91久久精品国产| 日韩av影院在线观看| 91久久久精品| 亚洲女人天堂成人av在线| 亚洲欧美视频在线| 欧美精品www在线观看| 国产狼人综合免费视频| 亚洲国产精品人人爽夜夜爽| 国产精品久久久久77777| 亚洲在线观看视频| 亚洲一区二区三区视频| 欧美成人免费视频| 精品国产乱码久久久久久天美| 午夜美女久久久久爽久久| 欧美国产日韩一区二区三区| 欧美性猛交xxxx富婆弯腰| 国产99久久精品一区二区永久免费| 国内精品一区二区三区四区| 亚洲综合中文字幕在线观看| 亚洲在线视频福利| 色一区av在线| 日韩理论片久久| 国内精品久久久久影院 日本资源| 成人做爰www免费看视频网站| 精品国偷自产在线| 亚洲精品资源美女情侣酒店| 国产免费观看久久黄| 久久精品99久久久香蕉| 另类专区欧美制服同性| 亚洲精品日韩久久久| 欧美国产日韩一区二区在线观看| 97在线视频免费观看| 国产999精品久久久| 日韩在线观看视频免费| 国产中文字幕亚洲| 91美女片黄在线观看游戏| 亚洲精品电影网| 亚洲成色www8888| 亚洲欧美综合区自拍另类| 国产精品日韩欧美大师| 狠狠躁夜夜躁久久躁别揉| 欧美在线中文字幕| 日韩免费在线视频| 伊人久久久久久久久久久久久| 亚洲第一免费网站| 亚洲一区二区中文| 欧美日韩第一视频| 日韩欧美高清在线视频| 欧美福利视频网站| 热久久99这里有精品| 成人黄色在线播放| 秋霞成人午夜鲁丝一区二区三区| 在线播放亚洲激情| 日韩av手机在线| 国产va免费精品高清在线观看| 色噜噜国产精品视频一区二区| 欧美大片免费看| 国产精品视频免费在线| 91精品在线影院| 国产精品免费在线免费| 国产一区视频在线播放| 久青草国产97香蕉在线视频| 久久精品国产一区二区三区| 亚洲精品v欧美精品v日韩精品| 成人在线免费观看视视频| 尤物yw午夜国产精品视频| 国产日韩欧美在线视频观看| 一区二区福利视频| 国产精品日韩在线播放| 国产日韩换脸av一区在线观看| 亚洲精品成人av| 亚洲国产日韩欧美在线动漫| 91性高湖久久久久久久久_久久99| 亚洲一区二区三区久久| 国产xxx69麻豆国语对白| 日韩av在线资源| 国产精品久久久久久亚洲影视| 欧美专区中文字幕| 欧美整片在线观看| 日本一区二区三区在线播放| 国产精品免费在线免费| 亚洲尤物视频网| 欧美大码xxxx| 久久视频国产精品免费视频在线| 久久久久久久国产精品视频| 久久久女人电视剧免费播放下载| 日韩美女写真福利在线观看| 亚洲欧美另类中文字幕| 国产深夜精品福利| 日韩毛片在线观看| 国产精品美女久久久久av超清| 日韩人在线观看| 川上优av一区二区线观看| 亚洲欧美国产va在线影院| 精品亚洲一区二区三区在线观看| 欧美电影电视剧在线观看| 中文字幕亚洲欧美一区二区三区| 精品少妇v888av| 久久久久亚洲精品| 欧美专区日韩视频| 91国内精品久久| 精品国产鲁一鲁一区二区张丽| 92福利视频午夜1000合集在线观看| 麻豆国产精品va在线观看不卡| 国产一区二区日韩| 亚洲欧洲一区二区三区在线观看| 亚洲午夜未满十八勿入免费观看全集| 日韩av免费观影| 亚洲毛片在线免费观看| 国产亚洲精品久久久久久| 在线免费看av不卡| 国产成人jvid在线播放| 91欧美视频网站| 亚洲欧美日韩成人| 亚洲欧美中文日韩v在线观看| 欧美另类99xxxxx| 欧美激情视频网站| 亚洲一区二区久久| 国产精品日韩av| 日韩黄色在线免费观看| 亚洲爱爱爱爱爱| 精品国内亚洲在观看18黄| 亚洲伊人久久大香线蕉av| 国产成人一区二区三区电影| 欧美在线欧美在线| 日本欧美国产在线| y97精品国产97久久久久久| 亚洲黄色av网站| 久久久久国产精品一区| 亚洲欧美日韩区| 久久视频免费观看| 92福利视频午夜1000合集在线观看| 一本色道久久综合狠狠躁篇怎么玩| 亚洲老司机av| 久久精品中文字幕免费mv| 伊人久久久久久久久久|