這篇文章帶給大家的內容是關於React Router知識的全面解析(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
多頁應用程式中,一個URL對應一個HTML頁面,一個Web應用程式包含許多HTML頁面,在多頁應用程式中,頁面路由控制由伺服器端負責,這種路由方式稱為後端路由。
多頁應用程式中,每次頁面切換都需要向伺服器發送一次請求,頁面使用到的靜態資源也需要重新加載,存在一定的浪費。而且,頁面的整體刷新對使用者體驗也有影響,因為不同頁間往往存在共同的部分,例如導覽列、側邊欄等,頁面整體刷新也會導致共用部分的刷新。
在單面應用程式中,URL發生並不會向伺服器發送新的請求,所以「邏輯頁面」的路由只能由前端負責,這種路由方式稱為前端路由。
目前,國內的搜尋引擎大多對單頁應用的SEO支援的不好,因此,對於SEO 非常看重的Web
應用(例如,企業官方網站,電商網站等),一般還是會選擇採用多頁面應用程式。 React 也並非只能用於開發單頁應用程式。
這裡使用的 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 同步。
http://example.com/some/path
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。
每個Route 都需要定義一個path 屬性,當使用BrowserRouter 時,path 用來描述這個Router符合的URL 的pathname;使用HashRouter時,path 用來描述這個Route 相符的URL 的hash。例如,使用 BrowserRouter 時,
當 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 的匹配的方式 (1)component component 的值是一个组件,当 URL 和 Route 匹配时,Component属性定义的组件就会被渲染。例如: Foo 组件接收了一个额外的 data 属性。 (3)children 如果 Route 匹配当前 URL,待渲染元素的根节点 p 的 class 将设置成 active. 当URL 和多个 Route 匹配时,这些 Route 都会执行渲染操作。如果只想让第一个匹配的 Route 沉浸,那么可以把这些 Route 包到一个 Switch 组件中。如果想让 URL 和 Route 完全匹配时,Route才渲染,那么可以使用 Route 的 exact 属性。Switch 和 exact 常常联合使用,用于应用首页的导航。例如: 如果不使用 Switch,当 URL 的 pathname 为 "/posts" 时, 嵌套路由是指在Route 渲染的组件内部定义新的 Route。例如,在上一个例子中,在 Posts 组件内再定义两个 Route: Link 是 React Router提供的链接组件,一个 Link 组件定义了当点击该 Link 时,页面应该如何路由。例如: Link 使用 to 属性声明要导航到的URL地址。to 可以是 string 或 object 类型,当 to 为 object 类型时,可以包含 pathname、search、hash、state 四个属性,例如: 除了使用Link外,我们还可以使用 history 对象手动实现导航。history 中最常用的两个方法是 push(path,[state]) 和 replace(path,[state]),push会向浏览器记录中新增一条记录,replace 会用新记录替换记录。例如: 路由设计的过程可以分为两步: 为每一个页面定义有语义的路由名称(path) 组织 Route 结构层次 我们有三个页面,按照页面功能不难定义出如下的路由名称: 登录页: /login 帖子列表页: /posts 帖子详情页: /posts/:id(id代表帖子的ID) 但是这些还不够,还需要考虑打开应用时的默认页面,也就是根路径"/"对应的页面。结合业务场景,帖子列表作为应用的默认页面为合适,因此,帖子列表对应两个路由名称: '/posts'和 '/' React Router 4并不需要在一个地方集中声明应用需要的所有 Route, Route实际上也是一个普通的 React 组件,可以在任意地方使用它(前提是,Route必须是 Router 的子节点)。当然,这样的灵活性也一定程度上增加了组织 Route 结构层次的难度。 第一个Route 使用了 exact 属性,保证只有当访问根路径时,第一个 Route 才会匹配成功。Home 是首页对应组件,可以通过 "/posts" 和 “/” 两个路径访问首页。注意,这里并没有直接渲染帖子列表组件,真正渲染帖子列表组件的地方在 Home 组件内,通过第二层级的路由处理帖子列表组件和帖子详情组件渲染,components/Home.js 的主要代码如下: 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 和它所有依赖的其他模块单独打包到一个chunk文件中,只有当用户点击加载按钮,才开始加载这个 chunk 文件。 asyncComponent接收一个函数参数 importComponent, importComponent 内通过import()语法动态导入模块。在AsyncComponent被挂载后,importComponent就会阴调用,进而触发动态导入模块的动作。 这样,只有当路由匹配时,对应的组件才会被导入,实现按需加载的效果。 这里还有一个需要注意的地方,打包后没有单独的CSS文件了。这是因为 CSS样子被打包到各个 chunk 文件中,当 chunk文件被加载执行时,会有动态把 CSS 样式插入页面中。如果希望把 chunk 中的 css打包到一个单独的文件中,就需要修改 webpack 使用的 ExtractTextPlugin 插件的配置,但 create-react-app 并没有直接把 webpack 的配置文件暴露给用户,为了修改相应配置 项目中会多出两个文件夹:config和 scripts,scrips中包含项目启动、编译和测试的脚本,config 中包含项目使用的配置文件, 然后重新编译项目,各个chunk 文件 使用的 CSS 样式 又会统一打包到 main.css 中。 以上是React Router知識的全面解析(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!3)Route 渲染组件的方式
<Route path='/foo' 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='/foo' render={(props) => {
<Foo {...props} data={extraProps} />
}}>
</Route>
children 的值也是一个函数,函数返回要渲染的 React 元素。 与前两种方式不同之处是,无论是否匹配成功, children 返回的组件都会被渲染。但是,当匹配不成功时,match 属性为 null。例如:<Route path='/foo' render={(props) => {
<p className={props.match ? 'active': ''}>
<Foo {...props} data={extraProps} />
</p>
}}>
</Route>
4)Switch 和 exact
<Router>
<Switch>
<Route exact path='/' component={Home}/>
<Route exact path='/posts' component={Posts} />
<Route exact path='/:user' component={User} />
</Switch>
</Router>
5)嵌套路由
const Posts = ({match}) => {
return (
<p>
{/* 这里 match.url 等于 /posts */}
<Route path={`${match.url}/:id`} component={PostDetail} />
<Route exact path={match.url} component={PostList} />
</p>
)
}
五、链接
const Navigation = () => {
<header>
<nav>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/posts'>Posts</Link></li>
</ul>
</nav>
</header>
}
<Link to={{
pathname: '/posts',
search: '?sort=name',
hash:'#the-hash',
state: { fromHome: true}
}}>
</Link>
history.push('/posts');
history.replace('/posts');
六、路由设计
1)定义路由名称
2)组织 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>
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>
)
}
}
七、代码分片
// 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;
当项目中使用 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 对上面的例子进行改造,代码如下: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;
,需要将 create-react-app 管理的配置文件“弹射”出来,在项目根路径下执行:npm run eject
webpack配置文件 就在这个路径下,打包 webpack.config.prod.js 找到配置 ExtractTextPlugin 的地方,添加 allChunks:true 这项配置:new ExtractTextPlugin({
filename: cssFilename,
allChunks: true
})