react 創(chuàng)建新的項目以及react-router4.xx 配置

背景日常絮叨

看到這個文章 大部分也是react 有基礎(chǔ)或者剛?cè)腴T的小伙伴,react 目前比較流行 歡欣的一個庫欢揖,下面讓我們一步一步run 起來

路由跳轉(zhuǎn)是指在同步保持瀏覽器URL的過程中渲染頁面中的視圖花颗。React Router 讓你聲明式的操作路由跳轉(zhuǎn)白魂。聲明式路由方法允許你控制應(yīng)用中的數(shù)據(jù)流.

創(chuàng)建新的react 項目

  • npm install -g create-react-app
  • create-react-app myapp //myapp 是你的項目名稱
  • cd myapp

但是打開項目會發(fā)現(xiàn)西土,一些與webpack相關(guān)的東西被隱藏掉了魄揉,只需要做單獨的配置鍵入下面的命令

  • npm run eject

在運行之前 可以安裝一個react-router

  • npm install --save react-router-dom
  • npm install 安裝依賴
  • npm run start
    沒有意外的情況下,都可以運行起來金赦,提醒下 如果 你修改 項目之后 在執(zhí)行npm run eject 可能會有報錯的情況音瓷,如果需要還是提早執(zhí)行。

為了方便管理復(fù)用夹抗,我一般都會拆分組件绳慎,src 里面 創(chuàng)建 文件page route component ,如下圖:

1.jpeg

react-router

  • 上面的都準(zhǔn)備好之后,我們就開始路由的配置杏愤,react-router靡砌,一般我們用到路由分為:
    • 路由的基本的跳轉(zhuǎn)
    • 嵌套路由
    • 帶路徑參數(shù)的嵌套路由

路由組件:BrowserRouter 和HashRouter
路徑匹配的組件: Route 和 Switch
導(dǎo)航組件: Link

  • 一般基礎(chǔ)的路由
export default (
  <Router history={historyConfig}>
   <div>
      <ul className="nav">
      <li><Link to="/">App</Link></li>
      <li><Link to="/About">About</Link></li>
      <li><Link to="/User">User</Link></li>
          <li><Link to="/Detail">Detail</Link></li>
     </ul>
    <hr />
    <Route exact path="/" component={Home} />
    <Route path="/About" component={About} />
    <Route path="/User" component={User} />
    <Route path="/Detail" component={DeatilComponent} />
</div>
  </Router>
);

Router組件決定了我們使用html5 history api,
Route組件定義了路由的匹配規(guī)則和渲染內(nèi)容珊楼,
使用 Link 組件進行路由之間的導(dǎo)航通殃。
使用 exact 屬性來定義路徑是不是精確匹配。

Router

我們都知道 React Router API 中有兩種類型的路由

<BrowserRouter> http://localhost:300/home 比較常用
<HashRouter> 哈希路由 http://localhost:300/#/home

  • 如果有服務(wù)器端的動態(tài)支持厕宗,建議使用 BrowserRouter画舌,否則建議使用 HashRouter。
import {
    BrowserRouter as Router,
    Route,
    Link
} from 'react-router-dom'

將 BrowserRouter 修改為 HashRouter 就可以了媳瞪,基本不需要修改其他東西骗炉。

Route常用屬性

主要職責(zé)是當(dāng)Route的位置和路徑匹配的時候渲染對應(yīng)的ui

exact、path以及component屬性
Route會向component組件傳一個參數(shù)蛇受,包含屬性match句葵,location,history兢仰。match屬性對象又包含url乍丈,path,params等屬性把将。比較常用的就是match的url屬性轻专,可以繼續(xù)基于url指定組件里面的Link標(biāo)簽要鏈接到的url,從而顯示對應(yīng)的組件察蹲。

Route寫在哪里请垛,當(dāng)Route匹配到URL的時候,相應(yīng)的組件就會在那里進行渲染洽议。component宗收,render,children亚兄,Route的這三個屬性寫一個就行混稽,不能同時都寫。precendence order: component > render > children.

注意:children中的元素不管是否匹配到URL都會渲染审胚,不過沒有匹配到的Route向children的函數(shù)中傳的值是null匈勋,只有匹配到的時候才會有值。

  • 橫向?qū)Ш綑冢菏褂肦outed 的 children屬性膳叨。
    側(cè)邊菜單欄:左邊是Link洽洁,右邊把Route寫在右邊展示區(qū)域的div里面,匹配到的時候進行渲染菲嘴。
<Route>是如何渲染的诡挂?

當(dāng)路由 match 成功之后碎浇,route 根據(jù)

  • 1、component : 一個React組件璃俗。當(dāng)帶有component參數(shù)的route匹配成功后,route會返回一個新的元素悉默,其為component參數(shù)所對應(yīng)的React組件(使用React.createElement創(chuàng)建)城豁。

  • 2、render : 一個返回React element的函數(shù)[注5]抄课。當(dāng)匹配成功后調(diào)用該函數(shù)唱星。該過程與傳入component參數(shù)類似,并且對于行級渲染與需要向元素傳入額外參數(shù)的操作會更有用跟磨。

  • 3间聊、children : 一個返回React element的函數(shù)。與上述兩個參數(shù)不同抵拘,無論route是否匹配當(dāng)前l(fā)ocation哎榴,其都會被渲染。


  • Route向component組件進行參數(shù)傳遞
    組件:
    const Topics = ( {match} ) => ()
    路由:
    <Route path="/topics" component={Topics}/>
    路由Route向組件傳的參數(shù):
    {history:{},
    location:{},
    match:{}}.
    執(zhí)行到 const Topics = ( {match} ) => ()的時候會將參數(shù)對象
    賦值給對象{match}僵蛛,所以此時組件實際接受的參數(shù)只有match對象尚蝌。
  • history 插件

$ npm install --save history
createBrowserHistory

import createBrowserHistory from 'history/createBrowserHistory';
const historyConfig = createBrowserHistory({
basename: '/' + AREA_ENV

});

history有三種使用方式:
  • createBrowserHistory 現(xiàn)代瀏覽器使用 目前用的最多的

createBrowserHistory({
basename: '', // 基鏈接
forceRefresh: false, // 是否強制刷新整個頁面
keyLength: 6, // location.key的長度
getUserConfirmation: (message,callback) => callback(window.confirm(message)) // 跳轉(zhuǎn)攔截函數(shù) })

  • createMemoryHistory 手機端使用

createMemoryHistory({
initialEntries: ['/'], // 初始載入路徑,和MemoryRouter中的initialEntries是一樣的 initialIndex: 0, // initialEntries初始載入索引
keyLength: 6, // location.key的長度
getUserConfirmation: null // 路由跳轉(zhuǎn)攔截函數(shù) })

  • createHashHistory 老版本瀏覽器使用充尉,暫不介紹

Switch :

渲染與該地址匹配的第一個子節(jié)點 <Route> 或者 <Redirect>飘言。外面包一層Switch 和不用Switch 有什么不同呢?

  • 用Switch 包含 只渲染一個路由驼侠,如果不用Switch則全部的路由都要渲染出來

下面的代碼 所有的路由 都會渲染出來姿鸿,那么如果有些需求比如 側(cè)欄,面包屑那么我們只能選擇一個 路由渲染出來

    <Route exact path="/" component={Home} />
    <Route path="/Detail" component={PrivateRoute}  />
    <Route path="/About" component={About} />
    <Route path="/User" component={User} />

所以使用 Switch 實現(xiàn)

<Switch>
    <Route exact path="/" component={Home} />
    <Route path="/Detail" component={PrivateRoute}  />
    <Route path="/About" component={About} />
    <Route path="/User" component={User} />
</Switch>

Link:

導(dǎo)航到指定的路由


react-router 的基本原理:

實現(xiàn)URl 和UI界面的同步倒源,其中在react-router中苛预,URL對應(yīng)location 對象,而UI對應(yīng)的是react components 來決定相速,這樣就是 location 和 components 的同步的問題碟渺。

  • react-router 主要依賴于history
// 內(nèi)部的抽象實現(xiàn)
function createHistory(options={}) {
  ...
  return {
    listenBefore, // 內(nèi)部的hook機制,可以在location發(fā)生變化前執(zhí)行某些行為突诬,AOP的實現(xiàn)
    listen, // location發(fā)生改變時觸發(fā)回調(diào)
    transitionTo, // 執(zhí)行l(wèi)ocation的改變
    push, // 改變location
    replace,
    go,
    goBack,
    goForward,
    createKey, // 創(chuàng)建location的key苫拍,用于唯一標(biāo)示該location,是隨機生成的
    createPath,
    createHref,
    createLocation, // 創(chuàng)建location
  }
}
  • 1旺隙、當(dāng)頁面路由發(fā)生變化的時候绒极;history 底層支持pushState, 將參數(shù)傳輸?shù)?createLocation 方法中,返回 location 的結(jié)構(gòu)如下:
location = {
  pathname, // 當(dāng)前路徑蔬捷,即 Link 中的 to 屬性
  search, // search
  hash, // hash
  state, // state 對象
  action, // location 類型垄提,在點擊 Link 時為 PUSH榔袋,瀏覽器前進后退時為 POP,調(diào)用 replaceState 方法時為 REPLACE
  key, // 用于操作 sessionStorage 存取 state 對象
};
  • 2铡俐、將location對象作為參數(shù)傳入到 TransitionTo方法中凰兑,執(zhí)行l(wèi)oaction 的變化,然后調(diào)用 window.location.hash 或者window.history.pushState() 修改了應(yīng)用的 URL, 同時觸發(fā)了 history.listen 的監(jiān)聽审丘。
  • 3吏够、更新location之后,然后系統(tǒng)調(diào)用 matchRoutes 方法配置 出與當(dāng)前l(fā)ocation對象匹配的一個子集滩报。
  • 4锅知、 匹配成功之后開始渲染 -> 渲染組件 更新UI(內(nèi)部具體經(jīng)過要后續(xù)研究)
檢測url 前進:
  • createBrowserHistory: pushState、replaceState
  • createHashHistory: location.hash=*** location.replace()
  • createMemoryHistory: 在內(nèi)存中進行歷史記錄的存儲

hashChange 監(jiān)聽window.location.hash 的變化脓钾,hash 發(fā)生變化售睹,瀏覽器更新url,同時history 棧中會產(chǎn)生一條新的記錄可训。
在 react-router 內(nèi)部注冊了 window.addEventListener('hashchange', listener, false) 事件監(jiān)聽器, listener 內(nèi)部可以通過 hash fragment 獲取到當(dāng)前 URL 對應(yīng)的 location 對象

pushState: window.history.pushState(state, title, path)方法 昌妹,改變?yōu)g覽器的url,通過location.state 來獲取到 state沉噩,在 react-router內(nèi)部將該對象存儲到了 sessionStorage 中

檢測url 回退:
  • createBrowserHistory: popstate
  • createHashHistory: hashchange
  • createMemoryHistory: 因為是在內(nèi)存中操作捺宗,跟瀏覽器沒有關(guān)系,不涉及UI層面的事情川蒙,所以可以直接進行歷史信息的回退

路由匹配原理:

路由有三個屬性決定是否匹配一個URL蚜厉;

  • 嵌套關(guān)系
  • 路徑語法
  • 優(yōu)先級

1、嵌套關(guān)系

當(dāng)一個給定的 URL 被調(diào)用時畜眨,整個集合中(命中的部分)都會被渲染昼牛。嵌套路由被描述成一種樹形結(jié)構(gòu)。React Router 會深度優(yōu)先遍歷整個路由配置來尋找一個與給定的 URL 相匹配的路由康聂。

2贰健、路徑語法

  • paramName – 匹配一段位于 /?# 之后的 URL恬汁。 命中的部分將被作為一個參數(shù)
  • () – 在它內(nèi)部的內(nèi)容被認(rèn)為是可選的
  • ** – 匹配任意字符(非貪婪的)直到命中下一個字符或者整個 URL 的末尾伶椿,并創(chuàng)建一個 splat 參數(shù)
<Route path="/hello/:name">         // 匹配 /hello/michael 和 /hello/ryan
<Route path="/hello(/:name)">       // 匹配 /hello, /hello/michael 和 /hello/ryan
<Route path="/files/*.*">           // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg

3、優(yōu)先級

路由是自頂向下匹配路由氓侧,確保前一個路由不會匹配后一個路由的路徑


BrowserRouter 和HashRouter 區(qū)別

BrowserRouter:

vue:mode:history
react: <BrowserRouter>

pushState脊另, replaceState會改變當(dāng)前路徑,但是他不會導(dǎo)致單頁面的重新渲染约巷,我們所使用時偎痛,頁面的渲染是由react或vue中的Router中監(jiān)聽了路由的變化

// 監(jiān)聽路由變化
this.unlisten = props.history.listen(location => {
  if (this._isMounted) {
      this.setState({ location });
  } else {
      this._pendingLocation = location;
  }
});
// 以下就是Route在當(dāng)路由發(fā)生變化時做的渲染
{props.match
  ? children
    ? typeof children === "function"
      ? __DEV__
        ? evalChildrenDev(children, props, this.props.path)
        : children(props)
      : children
    : component
    ? React.createElement(component, props)
    : render
    ? render(props)
    : null
  : typeof children === "function"
  ? __DEV__
    ? evalChildrenDev(children, props, this.props.path)
    : children(props)
  : null}

當(dāng)刷新頁面時,瀏覽器會向服務(wù)器請求example.com/list独郎,服務(wù)器實際會去找根目錄下list.html這個文件踩麦,發(fā)現(xiàn)找不到枚赡,因為實際上我們的服務(wù)器并沒有這樣的 物理路徑/文件 或沒有配置處理這個路由,所有內(nèi)容都是通過React-Router去渲染React組件谓谦,自然會報404錯誤贫橙。這種情況我們可以通過配置Nginx或通過自建Node服務(wù)器來解決。

hashHistory:

vue:mode:hash
react: <HashRouter>

從BrowserRouter.js和HashRouter.js文件中可以看到,history對象是由history插件生成的

// BrowserRouter.js
import { createBrowserHistory as createHistory } from "history";
history = createHistory(this.props);
 
// 用于createHistory傳入的配置對象參數(shù)鬼譬,也說明了這個配置是有父級傳遞的娜膘,而不是BrowserRouter自身的
BrowserRouter.propTypes = {
   basename: PropTypes.string,
   children: PropTypes.node,
   forceRefresh: PropTypes.bool,
   getUserConfirmation: PropTypes.func,
   keyLength: PropTypes.number
};
 
// HashRouter.js
import { createHashHistory as createHistory } from "history";
history = createHistory(this.props);
 
// 用于createHistory傳入的配置對象參數(shù)
HashRouter.propTypes = {
   basename: PropTypes.string,
   children: PropTypes.node,
   getUserConfirmation: PropTypes.func,
   hashType: PropTypes.oneOf(["hashbang", "noslash", "slash"])
};

createMemoryHistory:

  • Memory history 不會在地址欄被操作或讀取。這就解釋了我們是如何實現(xiàn)服務(wù)器渲染的优质。同時它也非常適合測試和其他的渲染環(huán)境(像 React Native )竣贪。和另外兩種history的一點不同是你必須創(chuàng)建它,這種方式便于測試巩螃。

React-router 按需加載方式:

一: create-react-app

  • 創(chuàng)建一個異步組件 AsyncComponent
import React from 'react';

export default function (getComponent) {
  return class AsyncComponent extends React.Component {
    static Component = null;
    state = { Component: AsyncComponent.Component };

    componentWillMount() {
      if (!this.state.Component) {
        getComponent().then(({default: Component}) => {
          AsyncComponent.Component = Component
          this.setState({ Component })
        })
      }
    }
    render() {
      const { Component } = this.state
      if (Component) {
        return <Component {...this.props} />
      }
      return null
    }
  }
}

  • 使用異步組件:我們將使用asyncComponent動態(tài)導(dǎo)入我們想要的組件演怎。
import asyncComponent from './asyncComponent'
const Login = asyncComponent(() => load('login/login'))
const LayoutPage = asyncComponent(() => load('layout/layout'))
const NoticeDeatil = asyncComponent(() => load('noticeDetail/noticeDetail'))
export const appRouterMap = [
    {path:"/login",name:"Login",component:Login,auth:false},
    {path:"/web",name:"LayoutPage",component:LayoutPage,auth:false},
    {path:"/notice/:id",name:"NoticeDeatil",component:NoticeDeatil,auth:false},
]

二、借助react-loadable來實現(xiàn)按需加載

1避乏、利用react-loadable這個高級組件爷耀,要做到實現(xiàn)按需加載這一點

第三種 bundle-loader 按需加載方式


不管vue 還是react 都可以使用

hash路由:

hash原理是觸發(fā)了onhashchange 事件,

hash —— 即地址欄 URL 中的 # 符號(此 hash 不是密碼學(xué)里的散列運算)拍皮。比如這個 URL:http://www.abc.com/#/hello歹叮,hash 的值為 #/hello。
它的特點在于:hash 雖然出現(xiàn)在 URL 中铆帽,但不會被包括在 HTTP 請求中咆耿,對后端完全沒有影響,因此改變 hash 不會重新加載頁面爹橱。

window.onhashchange = function(event){
    console.log(event.oldURL, event.newURL);
    let hash = location.hash.slice(1);
    document.body.style.color = hash; 
}

前端路由的核心萨螺,就在于 —— 改變視圖的同時不會向后端發(fā)出請求。

history路由:

history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法愧驱。(需要特定瀏覽器支持)這兩個方法應(yīng)用于瀏覽器的歷史記錄棧慰技,在當(dāng)前已有的 back、forward冯键、go 的基礎(chǔ)之上惹盼,它們提供了對歷史記錄進行修改的功能。只是當(dāng)它們執(zhí)行修改時惫确,雖然改變了當(dāng)前的 URL手报,但瀏覽器不會立即向后端發(fā)送請求蚯舱。

  • pushState() 設(shè)置的新 URL 可以是與當(dāng)前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分掩蛤,因此只能設(shè)置與當(dāng)前 URL 同文檔的 URL枉昏;

  • pushState() 設(shè)置的新 URL 可以與當(dāng)前 URL 一模一樣,這樣也會把記錄添加到棧中揍鸟;而 hash 設(shè)置的新值必須與原來不一樣才會觸發(fā)動作將記錄添加到棧中兄裂;

  • pushState() 通過 stateObject 參數(shù)可以添加任意類型的數(shù)據(jù)到記錄中;而 hash 只可添加短字符串阳藻;

history 模式下晰奖,前端的 URL 必須和實際向后端發(fā)起請求的 URL 一致,http://www.abc.com/book/id腥泥。如果后端缺少對 /book/id 的路由處理匾南,將返回 404 錯誤。
Vue-Router 官網(wǎng)里如此描述:“不過這種模式要玩好蛔外,還需要后臺配置支持……所以呢蛆楞,你要在服務(wù)端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態(tài)資源,則應(yīng)該返回同一個 index.html 頁面夹厌,這個頁面就是你 app 依賴的頁面豹爹。”

Vue-Router HTML5 History 模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矛纹,一起剝皮案震驚了整個濱河市臂聋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崖技,老刑警劉巖逻住,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異迎献,居然都是意外死亡瞎访,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門吁恍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扒秸,“玉大人,你說我怎么就攤上這事冀瓦“榘拢” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵翼闽,是天一觀的道長拾徙。 經(jīng)常有香客問我,道長感局,這世上最難降的妖魔是什么尼啡? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任暂衡,我火速辦了婚禮,結(jié)果婚禮上崖瞭,老公的妹妹穿的比我還像新娘狂巢。我一直安慰自己,他們只是感情好书聚,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布唧领。 她就那樣靜靜地躺著,像睡著了一般雌续。 火紅的嫁衣襯著肌膚如雪斩个。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天驯杜,我揣著相機與錄音萨驶,去河邊找鬼。 笑死艇肴,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叁温。 我是一名探鬼主播再悼,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼膝但!你這毒婦竟也來了冲九?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤跟束,失蹤者是張志新(化名)和其女友劉穎莺奸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冀宴,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡灭贷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了略贮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甚疟。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖逃延,靈堂內(nèi)的尸體忽然破棺而出览妖,到底是詐尸還是另有隱情,我是刑警寧澤揽祥,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布讽膏,位于F島的核電站,受9級特大地震影響拄丰,放射性物質(zhì)發(fā)生泄漏府树。R本人自食惡果不足惜俐末,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挺尾。 院中可真熱鬧鹅搪,春花似錦、人聲如沸遭铺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽魂挂。三九已至甫题,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涂召,已是汗流浹背坠非。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留果正,地道東北人炎码。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像秋泳,于是被迫代替她去往敵國和親潦闲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容