前言: css
路由放到前面說,同時也是我開始學習react最早想要搞懂的。由於咱們寫的前端頁面畢竟只有一個,隨着業務增長,或者根據UI設計圖,N個頁面,驅動N個頁面流通的即是<路由>不知道說法有沒有問題,我的理解。畢竟不少文檔講到前端路由,都會從他的起源開始說。前端
至少,路由搞懂之後,對於以後頁面的開發確定是有很大幫助的。本篇文章只講解或者學習react路由的相關知識以及應用。優化方面在後期項目作完打包的時候再作詳細調整。vue
優化這個好比vue有個路由懶加載模式,對應react確定也是有的【猜測】react
首先咱們須要瞭解下jsx 官方文檔也有相關描述 如下摘自官方文檔git
const element = <h1>Hello, world!</h1>;複製代碼
這個有趣的標籤語法既不是字符串也不是 HTML。github
它被稱爲 JSX,是一個 JavaScript 的語法擴展。咱們建議在 React 中配合使用 JSX,JSX 能夠很好地描述 UI 應該呈現出它應有交互的本質形式。JSX 可能會令人聯想到模版語言,但它具備 JavaScript 的所有功能。編程
JSX 能夠生成 React 「元素」。後端
Babel 會把 JSX 轉譯成一個名爲 React.createElement()
函數調用。
api
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);複製代碼
以上是徹底等效的兩段代碼,也就是說上面的寫法最終編譯成下面的一段代碼,所以,你寫的每一個頁面都必須引入react瀏覽器
import React from 'react'
同時 React.createElement會預檢查代碼。若是你使用過githook 就會知道。
以前作vue會引入husky 這個就是用來預檢查代碼。他會檢查如下幾種: 我只能想起來這幾種哈
1. xxxx is defined but not used 相似這個 就是定義了可是沒使用過
2. === instead of == 就是說你必須使用三等
eg:
Line 2:8: 'logo' is defined but never used no-unused-vars
這個就要求你在寫代碼的時候就要注重你的代碼質量。多餘的或者寫錯的。複製代碼
上篇文章有目錄結構,對於初始化的react項目是沒有router相關文件的,所以能夠本身創建一個router文件夾/index.js 用來編寫路由相關的,如下貼上路由文件相關配置
以上,是你能夠在任何相關文章中能夠找到的寫法。下來就是要解讀這些代碼
前兩句很簡單是引入頁面組件,。若是你寫過vue熟悉vue的話,這個不是問題。即便你沒有寫過,也不要緊。
例如home頁面中 class Home extends React.Component{} export default Home
對應 引入組件 就要用import 複製代碼
關於react-router-dom 你能夠查閱相關文檔
www.jianshu.com/p/97e4af328…
顧名思義,路徑會增長一個#的標識,這就是hash模式
HashRouter
組件,使用window.location.hash
和hashchange
事件構建路由。
Link
組件,會渲染一個a
標籤;
<Link to={your path}> </Link>複製代碼
用於渲染與路徑匹配的第一個子 <Route>
或 <Redirect>
。
這與僅僅使用一系列 <Route>
有何不一樣?
<Switch>
只會渲染一個路由。相反,僅僅定義一系列 <Route>
時,每個與路徑匹配的 <Route>
都將包含在渲染範圍內
<Route path="/home" component={home} /><Route path="/:value" component={value} />複製代碼
對於以上代碼,若是 訪問 /home, 則會都匹配上。也就意味着這些都會被渲染。而咱們想要的是 他對應的那個組件渲染。這個時候就須要switch。
所以,這個筆者認爲能夠約定俗成,加上switch來控制咱們的路由複製代碼
具體詳情能夠點擊查看上述連接。該做者的文章講解的比較詳細。
import Home from '../page/home/home'import Article from '../page/article/article'import React from 'react'import { Route, Switch, HashRouter,withRouter } from 'react-router-dom'class Router extends React.Component { constructor(props) { super(props) this.state = { } } render() { return ( <HashRouter> <Switch> <Route exact path="/" component={withRouter(Home)} /> <Route exact path="/article" component={Article} /> </Switch> </HashRouter> ) }}export default Router複製代碼
請仔細查看這段代碼,與上述截圖中。有一處是不一樣的。
沒錯就是 withRouter 你可能不知道這個是用來作什麼的。可是你確定能夠在其餘相關文章看到這樣的字眼。接下來要詳細解讀下 withRouter做用
withRouter : 把不是經過路由切換過來的組件中,將react-router 的 history、location、match 三個對象傳入props對象上
react中你能夠經過 this.props來獲取到路由相關參數。當你瀏覽器中控制檯輸出,就能夠看到如下結果。
可是有一種狀況你卻沒法獲取到props的值。下面給出栗子
首先我建立了一個組件 是頭部組件 header 用我以前整理好的目錄,寫於component中
接下來引入 app.js中
import React from 'react';import './App.css';import Router from './router/index'import Headers from './component/header/index';function App() { return ( <div className="App"> <Headers /> <Router /> </div> );}export default App;複製代碼
以上我在header這個組件中 輸出props 運行項目,你會發現控制輸出的props是空的。
這就麻煩了,我頭部會寫N個標籤來控制頁面跳轉的。天然而然少不了 標籤的動態樣式。
雖然用window.location能夠監聽到路由變化,可是我有強迫症,既然我要學習react,確定但願能用react的語法來解決。所以這裏就涉及到一個概念:
默認狀況下必須是通過路由匹配渲染的組件才存在this.props,才擁有路由參數,
才能使用編程式導航的寫法,執行this.props.history.push('/detail')跳轉到對應路由的頁面複製代碼
以上這段話很清晰了,上述的header是一個公共組件,不是用路由控制匹配渲染的組件,所以props是空的。因而呢? react-router-dom給出了一種解決方案,即是withRouter
開頭便寫出了withRouter的做用
因而乎,改動header頁面
import React from 'react'import {withRouter} from 'react-router-dom' // 引入withRouterclass Headers extends React.Component{ getProps() { console.log(this.props) console.log(window.location) } render() { return ( <div> 這是頭部 {this.getProps()}</div> ) }}export default withRouter(Headers)複製代碼
運行項目,???? 直接報錯
Error: Invariant failed: You should not use <withRouter(Headers) /> outside a <Router>複製代碼
因而又明白了一點: 這個withRouter 要 在router標籤對裏面 寫。、可他是一個公共組件,又不能寫在路由配置文件裏。有沒有一種辦法能兼容這二者呢?
若是你使用過vue,必定很瞭解 插槽這個東西。他是用來幹嗎的,很簡單,他是一個可定製化組件,這個組件能夠渲染任何你寫的內容。 方式是用<slot>
關於vue插槽,本篇不作詳細解釋,後續文章會作單獨解讀。
react是沒有插槽概念的,可是他也提供了一個語法: this.props.children
請看如下示例:
class RootContent extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<div className='divider'>
{this.props.children}
</div>
);
}
}
class RootChild extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<RootContent>
<p>Hello, React</p>
<p>Hello, Redux</p>
<p>Hello, Facebook</p>
<p>Hello, Google</p>
</RootContent>
);
}
}
ReactDOM.render(
<RootChild />,
document.querySelector('#root')
);
本案例摘自「https://www.jianshu.com/p/ea4eaf32cd46」 筆者由於時間緣故就省略號這一塊,用別人的案例來講明複製代碼
以上案例中,你就能夠發現,<RootContent> 這個組件內寫了一些內容,固然內容你能夠隨意改,他都會根據 <this.props.children> 渲染到頁面中,這就是react‘插槽的使用方式’
依照這個,對個人項目從新作調整,如下只摘錄了修改後的部分文件
筆者作個簡單介紹:
// app.js
import React from "react";import "./App.css";import PropTypes from "prop-types";import routers from "./router/router.js";import { HashRouter, Route, Switch } from "react-router-dom";import Layout from "./component/layout/layout.js";const App = () => { return ( <HashRouter> <main> <Switch> <Layout>
/* react循環渲染 可查閱文檔 */ {routers.map((item, index) => { return ( <Route path={item.path} exact={item.exact} component={item.component} key={index} /> ); })} </Layout> </Switch> </main> </HashRouter> );};export default App;複製代碼
筆者對路由也作了進一步封裝 下面代碼摘自 router/index.js
import Home from "../page/home/home.js";import Articles from "../page/article/article.js";import ArticleDetail from "../page/articleDetail/articleDetail";const config = [ { path: "/", component: Home, exact: true, }, { path: "/articles", component: Articles, exact: true, }, { path: "/article/detail/:value", component: ArticleDetail, exact: true, },];// class RouteMap extends React.Component {// render() {// return (// <HashRouter>// <main>// <Switch>// <Route path="/" exact component={Home} />// <Route path="/home" exact component={Home} />// <Route path="/article" exact component={Articles}></Route>// </Switch>// </main>// </HashRouter>// )// }// }export default config;這麼一來,路由配置頁面就徹底只是定義路由的部分了,若是你寫過vue的話,就會很明白了。
vue的就是相似如此的複製代碼
下面摘錄 layout
import React from "react";import "./index.scss";import Header from "../header/index";class Layout extends React.Component { render() { return ( <div className="home"> <div className="header"> <Header /> </div> <div className="main"> <div className="co">
/* this.props.children */ {this.props.children} </div> </div> </div> ); }}export default Layout;複製代碼
完成以上的操做之後你就能夠繼續運行項目,此時你即可以在header中獲取到 props的值了。你就能夠對路由作出監聽,從而定製化頭部標籤的動態樣式了。
筆者的思路:
依照this.props.children的使用方法,筆者決定寫一個組件layout,這個組件包含頭部和內容兩大模塊 、
而內容是用路由匹配動態渲染的,可是考慮到後期頁面若是增長了,太多,你不能寫不少
<route>標籤吧。因而採用react循環渲染的方式來調整路由。這麼一來,原先的路由文件就真的只是路由定義了。、
到此,路由算是初步配置完成,同時也解決了 非路由匹配組件 沒法獲取props的問題
最後,筆者也是初步開始寫做,寫做水平不是很好。見諒。若是你以爲對你有所幫助,麻煩關注點贊給個star吧。、
github.com/luoying122/…
項目會持續性寫,持續性更新。直到完成先後端業務以及部署上線。該項目爲個人我的技術博客。期待完成並公網訪問的一天,願與君共勉