react-router的項目實踐

前言

隨着單頁應用的發展,路由這個詞不斷出如今咱們的視線中,路由跳轉,嵌套路由,路由配置,路由懶加載等等,目前整理了業界的一些面試題,帶着問題去整理咱們須要學習的知識,面試題主要是react技術棧的,以下html

  • 介紹hash和history的區別
  • 如何配置React-Router
  • react-router怎麼實現路由切換
  • 路由的動態加載模塊

hash和history

什麼是hash模式

  • hash就是指URL的錨部分(從#號開始的部分)。hash也稱做錨點,自己是用來作頁面定位的,它可使對應id的元素顯示在可視區域內。
  • 因爲 hash 值變化不會致使瀏覽器向服務器發出請求,並且 hash 改變會觸發 hashchange 事件,瀏覽器的進後退也能對其進行控制,因此人們在 html5的history出現前,基本都是使用hash來實現前端路由的。
window.location.hash = 'product' // 設置 url 的 hash,會在當前url後加上 '#product'

console.log(window.location.hash) // '#product'  

// 監聽hash變化,點擊瀏覽器的前進後退會觸發
window.addEventListener('hashchange', function(){ 
    
})

// 或者在body上加上onhashchange
<body onhashchange="myFunction()">
  • react-router中hashHistory

Hash history 使用 URL 中的 hash(#)部分去建立形如 example.com/#/some/path 的路由。前端

  • hash模式過程

Hash Mode

什麼是history(HTML5的history對象)

History 接口容許操做瀏覽器的曾經在標籤頁或者框架裏訪問的會話歷史記錄(相似棧對象)。經常使用Api以下html5

  • History.back()

前往上一頁, 用戶可點擊瀏覽器左上角的返回按鈕模擬此方法. 等價於 history.go(-1).react

  • History.forward()

在瀏覽器歷史記錄裏前往下一頁,用戶可點擊瀏覽器左上角的前進按鈕模擬此方法. 等價於 history.go(1).git

  • History.go()

經過當前頁面的相對位置從瀏覽器歷史記錄( 會話記錄 )加載頁面。github

  • History.pushState()

按指定的名稱和URL(若是提供該參數)將數據push進會話歷史棧,數據被DOM進行不透明處理;面試

  • History.replaceState()

按指定的數據,名稱和URL(若是提供該參數),更新歷史棧上最新的入口。這個數據被DOM 進行了不透明處理。

附加window apitypescript

  • window.onpopstate

window.onpopstate是popstate事件在window對象上的事件處理程序.
每當處於激活狀態的歷史記錄條目發生變化時,popstate事件就會在對應window對象上觸發. 若是當前處於激活狀態的歷史記錄條目是由history.pushState()方法建立,或者由history.replaceState()方法修改過的, 則popstate事件對象的state屬性包含了這個歷史記錄條目的state對象的一個拷貝. npm

調用history.pushState()或者history.replaceState()不會觸發popstate事件. popstate事件只會在瀏覽器某些行爲下觸發, 好比點擊後退、前進按鈕(或者在JavaScript中調用history.back()、history.forward()、history.go()方法).redux

History Mode

如何配置React-router

目前咱們是基於create-react-app腳手架搭建起來的項目簡單配置的,直接上代碼

  • 1.搭建項目
npx create-react-app my-app --typescript
npm install --save react-router-dom
  • 2.在react-app-env.d.ts裏面聲明react-router-dom包或者安裝@types/react-router-dom解決找不到包的問題
declare module "react-router-dom";
  • 3.在src下面創建pages文件夾,建立Layout.tsx、Page1.tsx、Page2.tsx、Page3.tsx
// Layout.tsx
import * as React from "react";
import RouteView, { IRouteViewProps } from "../routes/RouteView";
import { History } from "history";

interface ILayoutProps extends IRouteViewProps {
  history: History;
}

const Layout = (props: ILayoutProps) => {
  const handleClick = React.useCallback((e) => {
    const { name } = e.target;
    props.history.push(name);
  }, [props.history]);

  return (
    <div>
      <div>
        <button name="/basic/page1" onClick={handleClick}>
          Page1
        </button>
        <button name="/basic/page2" onClick={handleClick}>
          Page2
        </button>
        <button name="/basic/page3" onClick={handleClick}>
          Page3
        </button>
      </div>
      <RouteView {...props} />
    </div>
  );
};

export default Layout;

// Page1.tsx
import * as React from "react";

const Page1 = () => {
    return (
        <div>我是Page1</div>
    )
};

export default Page1;

// Page2.tsx
import * as React from "react";

const Page2 = () => {
    return (
        <div>我是Page2</div>
    )
};

export default Page2;

// Page3.tsx
import * as React from "react";

const Page3 = () => {
    return (
        <div>我是Page3</div>
    )
};

export default Page3;
  • 4.在src下面創建routes文件夾,建立router.config.ts和RouteView.tsx
// router.config.ts
import Layout from "../pages/Layout";
import { lazy } from "react";
const RouteConfig = [
  {
    path: "/basic",
    component: Layout,
    children: [
      {
        path: "/basic/page1",
        component: lazy(() => import("../pages/Page1")),
      },
      {
        path: "/basic/page2",
        component: lazy(() => import("../pages/Page2")),
      },
      {
        path: "/basic/page3",
        component: lazy(() => import("../pages/Page3")),
      },
      { path: "/basic", redirect: "/basic/page1" },
    ],
  },
  // {
  //   path: "/login",
  //   component: lazy(() => import("../pages/Login")),
  // },
  {
    path: "/",
    redirect: "/basic",
  },
];

export default RouteConfig;

// RouteView.tsx
import React from "react";
import { Redirect, Route, Switch } from "react-router-dom";

export interface IRouteViewProps {
  path?: string;
  redirect?: string;
  component?: any;
  children?: IRouteViewProps[];
}

const RouteView = (props: IRouteViewProps) => {
  return (
    <Switch>
      {props.children &&
        props.children.map((item, index) => {
          if (item.redirect) {
            return (
              <Redirect
                key={index}
                from={item.path}
                to={item.redirect}
              ></Redirect>
            );
          }
          return (
            <Route
              key={index}
              path={item.path}
              render={(props) => {
                return (
                  item.component && (
                    <item.component
                      children={item.children}
                      {...props}
                    ></item.component>
                  )
                );
              }}
            ></Route>
          );
        })}
    </Switch>
  );
};

export default RouteView;
  • 5.修改App.tsx
// App.tsx
import React, { Suspense } from "react";
import RouteConfig from "./routes/router.config";
import RouteView from "./routes/RouteView";
import { BrowserRouter } from "react-router-dom";

const App = () => {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>loading...</div>}>
        <RouteView children={RouteConfig}></RouteView>
      </Suspense>
    </BrowserRouter>
  );
};

export default App;

一個具有路由嵌套,路由懶加載,可配置化的的React-Router就配置好了,代碼請參考:https://github.com/wwlh200/re...

react-router怎麼實現路由切換(基於實踐)

  • 若是引入了react-router-redux這個庫(包含使用dva的狀況,dva包含react-router-redux子庫)
const {history} = props;
history.push("/basic/product");
  • 若是不使用redux
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(Product));

const {history} = props;
history.push("/basic/other");
  • 若是使用react-hooks
import { useHistory } from "react-router-dom";

function HomeButton() {
  let history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

路由的動態加載模塊

  • React.lazy
const Component = React.lazy(() => import('./Component'));

缺點:不支持服務端渲染

  • loadable-components
// 第一步
npm install @loadable/component
// 第二步
const Component = loadable(() => import('./Component'));

缺點:須要引入第三方包

  • react-loadable
import Loadable from "react-loadable";
export default function asyncComponent(comp) {
  return Loadable({
    loader: comp,
    loading: (props) => {
       return "加載中...";
    },
  });
}

import asyncComponent from "../utils/utils";
const Component = asyncComponent(() => import('./Component'));

缺點:該方法不建議使用,StrictMode下回報以下警告:  The old API will be supported in all 16.x releases, but applications using it should migrate to the new version.

相關文章
相關標籤/搜索