一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - React - react自動化構建路由的實現

react自動化構建路由的實現

2022-03-09 16:07前端摸魚歐陽鋒 React

這篇文章主要介紹了react自動化構建路由的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

在使用react-router-dom在編寫項目的時候有種感覺就是,使用起來非常的方便,但是若是維護起來,那便是比較麻煩了,因為各大路由分散在各個組件中. 所以我們就會想到,使用react-router-dom中提供的config模式來編寫我們的路由,這樣寫的好處就是我們可以將邏輯集中在一處,配置路由比較方便

項目地址
https://gitee.com/d718781500/autoRouter

1.路由集中式

我們先將下列數據定義在/src/router/index.js中
在react的路由官方文檔中就提供了配置集中式路由的案例,大致是這樣的仿照vue的路由,生成一個配置文件,預期是這樣的

//需要一個路由的配置,它是一個數組
import Discover from "../pages/Discover"
import Djradio from "../pages/Discover/Djradio"
import Playlist from "../pages/Discover/Playlist"
import Toplist from "../pages/Discover/Toplist"
import Friends from "../pages/Friends"
import Mine from "../pages/Mine"
import Page404 from "../pages/Page404"
const routes = [
    {
        path: "/friends",
        component: Friends
    },
    {
        path: "/mine",
        component: Mine
    },
    
    {
        path: "/discover",
        component: Discover,
        children: [
            {
                path: "/discover/djradio",
                component: Djradio
            },
            {
                path: "/discover/playlist",
                component: Playlist

            },
            {
                path: "/discover/toplist",
                component: Toplist
            }
        ]
    },
    {//Page404這個配置一定要在所有路由配置之后
        path: "*",
        component: Page404
    }
]

export default routes

我們可以通過上述配置,來生成一個路由.當然上述的配置也只是做了簡單的處理,還有redirect exact等屬性沒有寫,我們還是從一個簡單的開始吧

2.文件目錄

上述的配置中使用了類似于vue的集中式路由配置模式,那么下面就展示下我當前這個demo的結構目錄吧

項目目錄結構

react自動化構建路由的實現

src/pages目錄結構

├─Discover
│  │  abc.js
│  │  index.js
│  │
│  ├─Djradio
│  │  │  index.js
│  │  │  lf.js
│  │  │
│  │  └─gv
│  │          index.js
│  │
│  ├─Playlist
│  │      index.js
│  │
│  └─Toplist
│          index.js
│
├─Entertaiment
│      index.js
│
├─Friends
│      index.js
│      xb.js
│
├─Mine
│      index.js
│
└─Page404
        index.js

有了這些結構之后,那么在1中提到的引入文件結合起來看就不懵逼啦,接下來我們可以封裝一個組件,給他取個名字叫做CompileRouter這個組件專門用于編譯路由

3.創建CompileRouter

react自動化構建路由的實現

這個組件我們把它創建在src/utils中,作用就是通過傳入的路由配置,然后計算出這個組件,那么問題來了,為什么要創建這個組件呢?

讓我們回顧一下react路由的編寫方式吧,react路由需要一個基礎組件HashRouter或者BrowserRouter這兩個相當于一個基石組件
然后還需要一個路由配方這個組件可以接受一個path映射一個component

我們來寫段偽代碼來說明一下

//引入路由基本組件(要在項目中安裝 npm i react-router-dom)
import {HashRouter as Router,Route} from "react-router-dom"
class Demo extends React.Component {
    render(){
        //基石路由
        <Router>
            //路由配方組件 通過path匹配component
            <Route path="/" component={Home}/>
             <Route path="/mine" component={Mine}/>
        </Router>
    }
}

這是基本用法,所以我們CompileRouter這個組件的工作就是,生成如上代碼中的Route一樣,生成Route然后展示在組件上
在了解到Compile的基本作用之后,下面我們就開始編碼吧
我個CompileRouter設計是接受一個數據,這個數據必須是符合路由配置的一個數組,就像1里代碼中所示的數組一樣,接受的屬性為routes

//這個文件通過routes配置來編譯出路由
import React from "react"
import { Switch, Route } from "react-router-dom";
export default class CompileRouter extends React.Component {
    constructor() {
        super()
        this.state = {
            c: []
        }
    }
    renderRoute() {
        let { routes } = this.props;//獲取routes路由配置
        //1.通過routes生成Route組件
        //確保routes是一個數組
        // console.log(routes)
        //render 不會重復讓組件的componentDidMount和componentWillUnmount重復調用
        if (Array.isArray(routes) && routes.length > 0) {
            //確保傳入的routes是個數組
           // 循環迭代傳入的routes
            let finalRoutes = routes.map(route => {
                //每個route是這個樣子的 {path:"xxx",component:"xxx"}
                //如果route有子節點 {path:"xxx",component:"xxx",children:[{path:"xxx"}]}
                return <Route path={route.path} key={route.path} render={
                       // 這么寫的作用就是,如果路由還有嵌套路由,那么我們可以把route中的children中的配置數據傳遞給這個組件,讓組件再次調用CompileRouter的時候就能編譯出嵌套路由了
                    () => <route.component routes={route.children} />
                } />
            })

            this.setState({
                c: finalRoutes
            })
        } else {
            throw new Error("routes必須是一個數組,并且長度要大于0")
        }
    }
    componentDidMount() {
        //確保首次調用renderRoute計算出Route組件
        this.renderRoute()
    }
    render() {
        let { c } = this.state;
        return (
            <Switch>
                {c}
            </Switch>
        )
    }
}

上述代碼就是用于去處理routes數據并且聲稱這樣的組件,每一步的作用我都已經在上面用注釋標明了

4.使用CompileRouter

其實我們可以把封裝的這個組件當成是vue-router中的視圖組件<router-view/>就暫且先這么認為吧,接下來我們需要在頁面上渲染1級路由了

在src/app.js

import React from "react"
import { HashRouter as Router, Link } from "react-router-dom"
//引入我們封裝的CompileRouter罪案
import CompileRouter from "./utils/compileRouter"
//引入在1中定義的路由配置數據
import routes from "./router"
console.log(routes)
class App extends React.Component {
    render() {
        return (
            <Router>
                <Link to="/friends">朋友</Link>
                |
                <Link to="/discover">發現</Link>
                |
                <Link to="/mine">我的</Link>
                {/*當成是vue-router的視圖組件 我們需要將路由配置數據傳入*/}
                <CompileRouter routes={routes} />
            </Router>
        )
    }
}

export default App

寫完后,那么頁面上其實就可以完美的展示1級路由了

5.嵌套路由處理

上面我們已經對1級路由進行了渲染,可以跳轉,但是二級路由怎么處理呢?其實也很簡單,我們只需要找到二級路由的父路由,繼續使用CompileRouter就可以了
我們從配置中可以看到,Discover這個路由是具有嵌套路由的,所以我們就以Discover路由為例子,首先我們看下結構圖

react自動化構建路由的實現

圖上的index.js就是Discover這個視圖組件了,也是嵌套路由的父級路由,所以我們只需要在這個index.js中繼續使用CompileRouter就可以了

import React from "react"
import { Link } from "react-router-dom"

import CompileRouter from "../../utils/compileRouter"
function Discover(props) {

    let { routes } = props //這個數據是從ComileRouter組件編譯的時候傳遞過來的children
    // console.log(routes)
    let links = routes.map(route => {
        return (
            <li key={route.path}>
                <Link to={route.path}>{route.path}</Link>
            </li>
        )
    })
    return (
        <fieldset>
            <legend>發現</legend>
            <h1>我發現,不能說多喝熱水</h1>
            <ul>
                {links}
            </ul>
            {/*核心代碼,再次使用即可 這里將通過children數據可以渲染出Route*/}
            <CompileRouter routes={routes} />
        </fieldset>
    )
}
Discover.meta = {
    title: "發現",
    icon: ""
}
export default Discover

所以我們以后記住,只要是有嵌套路由我們要做兩件事

  • 配置routes
  • 在嵌套路由的父級路由中再次使用CompileRouter,并且傳入routes即可

6. require.context

上面我們實現了一個路由集中式的配置,但是我們會發現一個問題

react自動化構建路由的實現

引入了很多的組件,實際上,在項目中引入的更多,如果一個一個引入,對我們來說是災難性的,所以我們可以使用webpack提供的一個很好用的api,require.context我們先說說它是怎么使用的吧
自動化導入require.context方法,使用這個方法可以減少繁瑣的組件引入,而且可以深度的遞歸目錄,做到import做不到的事情 下面我們來看一下這個方法是如何使用的

使用

你可以通過 require.context() 函數來創建自己的 context。
可以給這個函數傳入4個參數:

  • 一個要搜索的目錄,
  • 一個標記表示是否還要搜索其子目錄,
  • 一個匹配文件的正則表達式。
  • mode  模塊加載模式,常用值為 sync、lazy、lazy-once、eager
    • sync 直接打包到當前文件,同步加載并執行
    • lazy 延遲加載會分離出單獨的 chunk 文件
    • lazy-once 延遲加載會分離出單獨的 chunk 文件,加載過下次再加載直接讀取內存里的代碼。
    • eager 不會分離出單獨的 chunk 文件,但是會返回 promise,只有調用了 promise 才會執行代碼,可以理解為先加載了代碼,但是我們可以控制延遲執行這部分代碼。

webpack 會在構建中解析代碼中的 require.context() 。

語法如下:

require.context(
  directory,
  (useSubdirectories = true),
  (regExp = /^./.*$/),
  (mode = "sync")
);

示例:

require.context("./test", false, /.test.js$/);
//(創建出)一個 context,其中文件來自 test 目錄,request 以 `.test.js` 結尾。
require.context("../", true, /.stories.js$/);
// (創建出)一個 context,其中所有文件都來自父文件夾及其所有子級文件夾,request 以 `.stories.js` 結尾。

api

函數有三個屬性:resolve, keys, id。

resolve 是一個函數,它返回 request 被解析后得到的模塊 id。

let p = require.context("...",true,"xxx")
p.resolve("一個路徑")

keys 也是一個函數,它返回一個數組,由所有可能被此 context module 處理的請求(譯者注:參考下面第二段代碼中的 key)組成。

require.context的返回值是一個函數,我們可以在函數中傳入文件的路徑,就可以得到模塊化的組件了

let components = require.context("../pages", true, /.js$/, "sync")

let paths = components.keys()//獲得了所有引入文件的地址
// console.log(paths)
let routes = paths.map(path => {
    let component = components(path).default
    path = path.substr(1).replace(//w+.js$/,"")
    return {
        path,
        component
    }
})
console.log(routes)

總結

雖然上面有很多api和返回的值,我們只拿兩個來做說明

keys方法,這個可以獲取所有模塊的路徑,返回的是一個數組

let context = require.context("../pages", true, /.js$/);

let paths = context.keys()//獲取了所有文件的路徑

獲取路徑下所有的模塊

let context = require.context("../pages", true, /.js$/);

let paths = context.keys()//獲取了所有文件的路徑

let routes = paths.map(path => {
    //批量獲取引入的組件
    let component = context(path).default;
    console.log(component)
    })

掌握這兩個就可以了,下面我們來繼續處理

7.扁平數據轉換為樹形結構的(convertTree算法)

這個算法的名字是我自己起的,首先我們要明白為甚么需要將數據轉換成tree
我們的預期的routes數據應該是下面這樣的

//目的是什么?
//生成一個路由配置
 const routes = [
     {
         path: "",
         component:xxx
          children:[
                 {
                     path:"xxx"
                     component:xxx
                 }
            ]
     }
 ]

但其實我們使用require.context處理之后的數據是這樣的

react自動化構建路由的實現

可以看到這個數據是完全扁平化的,沒有任何的嵌套,所以我們第一步就是要實現將這種扁平化的數據轉換為符合我們預期的樹形結構,下面我們一步一步來

7.1使用require.context將數據處理成扁平化

首先要處理成上圖那樣的結構,代碼都有注釋,難度也不高

//require.context()

// 1. 一個要搜索的目錄,
// 2. 一個標記表示是否還要搜索其子目錄, 
// 3. 一個匹配文件的正則表達式。
let context = require.context("../pages", true, /.js$/);

let paths = context.keys()//獲取了所有文件的路徑


let routes = paths.map(path => {
    //批量獲取引入的組件
    let component = context(path).default;
    //組件擴展屬性方便渲染菜單
    let meta = component["meta"] || {}
    //console.log(path)
    //這個正則的目的
    //因為地址是./Discover/Djradio/index.js這種類型的并不能直接使用,所以要進行處理
    //1.接去掉最前的"." 得到的結果是/Discover/Djradio/index.js
    //2.處理了還是不能直接用 因為我們的預期/Discover/Djradio,所以通過正則將index.js干掉了
    //3.有可能后面的路徑不是文件夾 得到的結果是/Discover/abc.js,后綴名并不能用到路由配置的path屬性中,所以.js后綴名又用正則替換掉
    path = path.substr(1).replace(/(/index.js|.js)$/, "")
    // console.log(path)
    return {
        path,
        component,
        meta
    }
})

7.2 實現convertTree算法

上面處理好了數據后,我們封裝一個方法,專門用于處理扁平化數據變成樹形數據,算法時間復雜度為O(n^2)

function convertTree(routes) {
    let treeArr = [];
    //1.處理數據 將每條數據的id和parent處理好 (俗稱 爸爸去哪兒了)
    routes.forEach(route => {
        let comparePaths = route.path.substr(1).split("/")
        // console.log(comparePaths)
        if (comparePaths.length === 1) {
            //說明是根節點,根節點不需要添加parent_id
            route.id = comparePaths.join("")
        } else {
            //說明具有父節點
            //先處理自己的id
            route.id = comparePaths.join("");
            //comparePaths除去最后一項就是parent_id
            comparePaths.pop()
            route.parent_id = comparePaths.join("")
        }
    })
    //2.所有的數據都已經找到了父節點的id,下面才是真正的找父節點了
    routes.forEach(route => {
        //判斷當前的route有沒有parent_id
        if (route.parent_id) {
            //有父節點
            //id===parent_id的那個route就是當前route的父節點
            let target = routes.find(v => v.id === route.parent_id);
            //判斷父節點有沒有children這個屬性
            if (!target.children) {
                target.children = []
            }
            target.children.push(route)
        } else {
            treeArr.push(route)
        }
    })

    return treeArr
}

通過上述處理之后就可以得到樹形結構啦

react自動化構建路由的實現

接下來我們只需要把數據導出去,在app上引入傳遞給CompileRouter組件就可以了

7.3 以后要注意的

以后只需要在pages中創建文件即可自動實現路由的處理以及編譯了,不過對于嵌套級別的路由咱們別忘了要在路由組件加上CompileRouter組件,總結為亮點

  • 創建路由頁面
  • 嵌套路由的父級路由組件中加入

8.擴展靜態屬性

我們當前創建出來的效果是有了,但是如果我們用于渲染菜單的時候就會有問題,沒有內容可以用于渲染菜單,所以我們可以給組件上擴展靜態屬性meta(也可以是別的),然后對我們的自動化編譯代碼做一些小小的改動就行了

組件

react自動化構建路由的實現

自動化處理邏輯完整代碼

//require.context()

// 1. 一個要搜索的目錄,
// 2. 一個標記表示是否還要搜索其子目錄, 
// 3. 一個匹配文件的正則表達式。
let context = require.context("../pages", true, /.js$/);

let paths = context.keys()//獲取了所有文件的路徑


let routes = paths.map(path => {
    //批量獲取引入的組件
    let component = context(path).default;
    //組件擴展屬性方便渲染菜單
    let meta = component["meta"] || {}
    //console.log(path)
    //這個正則的目的
    //因為地址是./Discover/Djradio/index.js這種類型的并不能直接使用,所以要進行處理
    //1.接去掉最前的"." 得到的結果是/Discover/Djradio/index.js
    //2.處理了還是不能直接用 因為我們的預期/Discover/Djradio,所以通過正則將index.js干掉了
    //3.有可能后面的路徑不是文件夾 得到的結果是/Discover/abc.js,后綴名并不能用到路由配置的path屬性中,所以.js后綴名又用正則替換掉
    path = path.substr(1).replace(/(/index.js|.js)$/, "")
    // console.log(path)
    return {
        path,
        component,
        meta
    }
})
//這種數據是扁平化的數據,并不符合我們的路由規則
//需要做算法 盡可能將時間復雜度降低o(n)最好
//封裝一個convertTree算法 時間復雜度o(n^2)
// console.log(routes)

//id
//parent_id

function convertTree(routes) {
    let treeArr = [];
    //1.處理數據 將每條數據的id和parent處理好 (俗稱 爸爸去哪兒了)
    routes.forEach(route => {
        let comparePaths = route.path.substr(1).split("/")
        // console.log(comparePaths)
        if (comparePaths.length === 1) {
            //說明是根節點,根節點不需要添加parent_id
            route.id = comparePaths.join("")
        } else {
            //說明具有父節點
            //先處理自己的id
            route.id = comparePaths.join("");
            //comparePaths除去最后一項就是parent_id
            comparePaths.pop()
            route.parent_id = comparePaths.join("")
        }
    })
    //2.所有的數據都已經找到了父節點的id,下面才是真正的找父節點了
    routes.forEach(route => {
        //判斷當前的route有沒有parent_id
        if (route.parent_id) {
            //有父節點
            //id===parent_id的那個route就是當前route的父節點
            let target = routes.find(v => v.id === route.parent_id);
            //判斷父節點有沒有children這個屬性
            if (!target.children) {
                target.children = []
            }
            target.children.push(route)
        } else {
            treeArr.push(route)
        }
    })

    return treeArr
}

export default convertTree(routes)

//獲取一個模塊
// console.log(p("./Discover/index.js").default)

//目的是什么?
//生成一個路由配置
// const routes = [
//     {
//         path: "",
//         component,
//          children:[
//                 {path component}
//             ]
//     }
// ]

寫在最后

其實上述的處理并不能作為應用級別用于項目中,主要在于CompileRouter處理的不夠細致,下一期我將專門寫一篇如何處理CompileRouter用于鑒權等應用在項目中

到此這篇關于react自動化構建路由的實現的文章就介紹到這了,更多相關react自動化構建路由內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://juejin.cn/post/6953933167321415716

延伸 · 閱讀

精彩推薦
  • Reactreact實現瀏覽器自動刷新的示例代碼

    react實現瀏覽器自動刷新的示例代碼

    這篇文章主要介紹了react實現瀏覽器自動刷新的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    LouisWK10702022-03-09
  • React淺談React Router關于history的那些事

    淺談React Router關于history的那些事

    這篇文章主要介紹了淺談React Router關于history的那些事,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們...

    這是上帝的杰作12042022-02-23
  • React深入理解React Native核心原理(React Native的橋接(Bridge)

    深入理解React Native核心原理(React Native的橋接(Bridge)

    這篇文章主要介紹了深入理解React Native核心原理(React Native的橋接(Bridge),本文重點給大家介紹React Native的基礎知識及實現原理,需要的朋友可以參考下...

    Gavell9612022-02-23
  • Reactreact顯示文件上傳進度的示例

    react顯示文件上傳進度的示例

    這篇文章主要介紹了react顯示文件上傳進度的示例,幫助大家更好的理解和學習使用react,感興趣的朋友可以了解下...

    阿政想暴富8762022-03-03
  • React使用 React 和 Threejs 創建一個VR全景項目的過程詳解

    使用 React 和 Threejs 創建一個VR全景項目的過程詳解

    這篇文章主要介紹了使用 React 和 Threejs 創建一個VR全景項目的過程詳解,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒...

    Windy Z11122022-02-23
  • Reactreact實現Radio組件的示例代碼

    react實現Radio組件的示例代碼

    這篇文章主要介紹了react實現Radio組件的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面...

    優雅的王德奧3672022-02-24
  • ReactReact中setState的使用與同步異步的使用

    React中setState的使用與同步異步的使用

    這篇文章主要介紹了React中setState的使用與同步異步的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    一顆冰淇淋5302022-02-17
  • ReactReact.Children的用法詳解

    React.Children的用法詳解

    這篇文章主要介紹了React.Children的用法詳解,幫助大家更好的理解和學習使用React框架,感興趣的朋友可以了解下...

    uuihoo10732022-02-23
主站蜘蛛池模板: 国产一精品一av一免费爽爽 | 三体动漫在线观看免费完整版2022 | 日韩视频第二页 | 教室里老师好紧h | 亚洲精品综合一二三区在线 | 成人网中文字幕色 | 久久国产精品永久免费网站 | 国产思妍小仙女一二区 | 极品主播的慰在线播放 | 我和老丈洗澡同性 | 末代皇帝无删减版在线观看 | 91桃色网站| 精灵之森高清在线 | 女王调奴丨vk | 青青草在视线频久久 | 精品无码一区二区三区中文字幕 | 日本xxx18hd | 奇米影视在线视频8888 | 精品国产免费观看一区高清 | 草草免费观看视频在线 | 免费看男女污污完整版 | 暖暖日本高清 | 3d动漫免费 | yellow视频在线观看免费 | 欧美成黑人性猛交xxoo | 国产成人精品一区二区仙踪林 | 国产成人免费高清激情明星 | 日b在线观看| 痴mu动漫成年动漫在线观看 | 精品手机在线视频 | 非洲黑人又大粗gay 非洲黑人bbwbbwbbw | 日本精品人妖shemale人妖 | 护士让我吃奶我扒她奶 | 午夜在线观看免费完整直播网 | 国产亚洲人成网站天堂岛 | 日韩一区国产二区欧美三 | 青久久| ck7788免费视频| 亚洲性色永久网址 | 成人免费福利网站在线看 | 亚洲精品综合一区二区 |