国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Router入門0x202: 自己實(shí)現(xiàn) Router 頁(yè)面調(diào)度和特定頁(yè)面訪問(wèn)

dance / 3485人閱讀

摘要:概述上一章講了如何實(shí)現(xiàn)組件頁(yè)面切換,這一章講如何解決上一章出現(xiàn)的問(wèn)題以及如何優(yōu)雅的實(shí)現(xiàn)頁(yè)面切換。在中監(jiān)聽(tīng)了事件,這樣就可以在變化的時(shí)候,需要路由配置并調(diào)用。

0x000 概述

上一章講了SPA如何實(shí)現(xiàn)組件/頁(yè)面切換,這一章講如何解決上一章出現(xiàn)的問(wèn)題以及如何優(yōu)雅的實(shí)現(xiàn)頁(yè)面切換。

0x001 問(wèn)題分析

回顧一下上一章講的頁(yè)面切換,我們通過(guò)LeactDom.render(new ArticlePage(),document.getElementById("app"))來(lái)切換頁(yè)面,的確做到了列表頁(yè)和詳情頁(yè)的切換,但是我們可以看到,瀏覽器的網(wǎng)址始終沒(méi)有變化,是http://localhost:8080,那如果我們希望直接訪問(wèn)某個(gè)頁(yè)面,比如訪問(wèn)某篇文章呢?也就是我們希望我們?cè)L問(wèn)的地址是http://localhost:8080/article/1,進(jìn)入這個(gè)地址之后,可以直接訪問(wèn)id 為1的文章。
問(wèn)題1:無(wú)法做到訪問(wèn)特定頁(yè)面并傳遞參數(shù)
問(wèn)題2:通過(guò)LeactDom.render(new ArticlePage(),document.getElementById("app"))太冗余了

0x002 簡(jiǎn)單的路由實(shí)現(xiàn)并和原生js結(jié)合:
基本上也是基于發(fā)布-訂閱模式,
register: 注冊(cè)路由
push: 路由跳轉(zhuǎn)

源碼

class Router {
    static routes = {}

    /**
     * 如果是數(shù)組
     * 就遍歷數(shù)組并轉(zhuǎn)化成 {"/index":{route:{...},callback:()=>{....}}} 形式
     * 并執(zhí)行 init 方法
     * 如果是對(duì)象
     * 就轉(zhuǎn)化成 {"/index":{route:{...},callback:()=>{....}}} 形式
     * 并和原來(lái)的 this.route 合并
     * 注意: 如果用對(duì)象形式必須手動(dòng)執(zhí)行 init 方法
     * 最終 this.route 形式為
     * [
     *  {"/index":{route:{...},callback:()=>{....}}}
     *  {"/detail":{route:{...},callback:()=>{....}}}
     * ]
     * @param routes
     * @param callback
     */
    static register(routes, callback) {
        if (Array.isArray(routes)) {
            this.routes = routes.map(route => {
                return {
                    [route.path]: {
                        route: route,
                        callback: callback
                    }
                }
            }).reduce((r1, r2) => {
                return {...r1, ...r2}
            })
        }
        this.routes = {
            ...this.routes,
            ...{
                [routes.path]: {
                    route: routes,
                    callback: callback
                }
            }
        }
    }

    /**
     * 跳轉(zhuǎn)到某個(gè)路由
     * 本質(zhì)是遍歷所有路由并執(zhí)行 callback
     *
     * @param path
     * @param data
     */
    static push(path, data) {
        Object.values(this.routes).forEach(route => {
            route.callback(data, this.routes[ path].route, path)
        })
    }

}

export default Router

使用

import Router from "./core/Router";

Router.register([
    {
        path: "/index",
        name: "主頁(yè)",
        component: (props) => {
            return document.createTextNode(`這是${props.route.name}`)
        }
    },
    {
        path: "/detail",
        name: "詳情頁(yè)",
        component: (props) => {
            return document.createTextNode(`這是${props.route.name}`)
        }
    }
], (data, route, match) => {
    if (route.path === match) {
        let app = document.getElementById("app")
        app.childNodes.forEach(c => c.remove())
        app.appendChild(new route.component({data,route,match}))
    }
})

Router.push("/index")

setTimeout(()=>{
    Router.push("/detail")
},3000)

說(shuō)明:
當(dāng)push方法調(diào)用的時(shí)候,會(huì)觸發(fā)register的時(shí)候傳入的callback,并找到push傳入的path匹配的路由信息,然后將該路由信息作為callback的參數(shù),并執(zhí)行callback。
在上面的流程中,我們注冊(cè)了兩個(gè)路由,每個(gè)路由的配置信息大概包含了path、namecomponent三個(gè)鍵值對(duì),但其實(shí)只有path是必須的,其他的都是非必須的,可以結(jié)合框架、業(yè)務(wù)來(lái)傳需要的參數(shù);在注冊(cè)路由的同時(shí)傳入了路由觸發(fā)時(shí)的動(dòng)作。這里設(shè)定為將父節(jié)點(diǎn)的子節(jié)點(diǎn)全部移除后替換為新的子節(jié)點(diǎn),也就達(dá)到了組件切換的功能,通過(guò)callbackprops參數(shù),我們可以獲取到當(dāng)前觸發(fā)的路由配置和觸發(fā)該路由配置的時(shí)候的數(shù)據(jù),比如后面調(diào)用Route.push("/index",{name:1})的時(shí)候,callbackprops

{
    data:{
        name:1
    },
    route:{ 
        path: "/index",
        name: "主頁(yè)",
        component: (props) => {
                return document.createTextNode(`這是${props.route.name}`)
            }
    }
}

0x003 和上一章的SPA結(jié)合
import Router from "./core/Router";
import DetailPage from "./page/DetailPage";
import ArticlePage from "./page/ArticlePage";
import LeactDom from "./core/LeactDom";

Router.register([
    {
        path: "/index",
        name: "主頁(yè)",
        component: ArticlePage
    },
    {
        path: "/detail",
        name: "詳情頁(yè)",
        component: DetailPage
    }
], (data, route,match) => {
    if (route.path !== match) return
    LeactDom.render(new route.component(data), document.getElementById("app"))
})

然后在頁(yè)面跳轉(zhuǎn)的地方,修改為Route跳轉(zhuǎn)

    // ArticlePage#componentDidMount
    componentDidMount() {
            let articles = document.getElementsByClassName("article")
            ;[].forEach.call(articles, article => {
                    article.addEventListener("click", () => {
                        // LeactDom.render(new DetailPage({articleId: article.getAttribute("data-id")}), document.getElementById("app"))
                        Router.push("/detail",{articleId:article.getAttribute("data-id")})
                    })
                }
            )
    
        }

    // DetailPage#componentDidMount
    componentDidMount() {
        document.getElementById("back").addEventListener("click", () => {
            LeactDom.render(new ArticlePage(), document.getElementById("app"))
            Router.push("/index")
        })
    }
0x004 指定跳轉(zhuǎn)頁(yè)面-hash

先看結(jié)果,我們希望我們?cè)谠L問(wèn)http://localhost:8080/#detail?articleId=2的時(shí)候跳轉(zhuǎn)到id=2的文章的詳情頁(yè)面,所以我們需要添加幾個(gè)方法:

    import Url from "url-parse"

class Router {
    static routes = {}

    /**
     * 初始化路徑
     * 添加 hashchange 事件, 在 hash 發(fā)生變化的時(shí)候, 跳轉(zhuǎn)到相應(yīng)的頁(yè)面
     * 同時(shí)根據(jù)訪問(wèn)的地址初始化第一次訪問(wèn)的頁(yè)面
     *
     */
    static init() {
        Object.values(this.routes).forEach(route => {
            route.callback(this.queryStringToParam(), this.routes["/" + this.getPath()].route,"/"+this.getPath())
        })

        window.addEventListener("hashchange", () => {
            Object.values(this.routes).forEach(route => {
                route.callback(this.queryStringToParam(), this.routes["/" + this.getPath()].route,"/"+this.getPath())
            })
        })

    }

    /**
     * 如果是數(shù)組
     * 就遍歷數(shù)組并轉(zhuǎn)化成 {"/index":{route:{...},callback:()=>{....}}} 形式
     * 并執(zhí)行 init 方法
     * 如果是對(duì)象
     * 就轉(zhuǎn)化成 {"/index":{route:{...},callback:()=>{....}}} 形式
     * 并和原來(lái)的 this.route 合并
     * 注意: 如果用對(duì)象形式必須手動(dòng)執(zhí)行 init 方法
     * 最終 this.route 形式為
     * [
     *  {"/index":{route:{...},callback:()=>{....}}}
     *  {"/detail":{route:{...},callback:()=>{....}}}
     * ]
     * @param routes
     * @param callback
     */
    static register(routes, callback) {
        if (Array.isArray(routes)) {
            this.routes = routes.map(route => {
                return {
                    [route.path]: {
                        route: route,
                        callback: callback
                    }
                }
            }).reduce((r1, r2) => {
                return {...r1, ...r2}
            })
            this.init()
        }
        this.routes = {
            ...this.routes,
            ...{
                [routes.path]: {
                    route: routes,
                    callback: callback
                }
            }
        }
    }

    /**
     * 跳轉(zhuǎn)到某個(gè)路由
     * 其實(shí)只是簡(jiǎn)單的改變 hash
     * 觸發(fā) hashonchange 函數(shù)
     *
     * @param path
     * @param data
     */
    static push(path, data) {
        window.location.hash = this.combineHash(path, data)
    }

    /**
     * 獲取路徑
     * 比如 #detail => /detail
     * @returns {string|string}
     */
    static getPath() {
        let url = new Url(window.location.href)
        return url.hash.replace("#", "").split("?")[0] || "/"
    }

    /**
     * 將 queryString 轉(zhuǎn)化成 參數(shù)對(duì)象
     * 比如 ?articleId=1 => {articleId: 1}
     * @returns {*}
     */
    static queryStringToParam() {
        let url = new Url(window.location.href)
        let hashAndParam = url.hash.replace("#", "")
        let arr = hashAndParam.split("?")
        if (arr.length === 1) return {}
        return arr[1].split("&").map(p => {
            return p.split("=").reduce((a, b) => ({[a]: b}))
        })[0]
    }

    /**
     * 將參數(shù)變成 queryString
     * 比如 {articleId:1} => ?articleId=1
     * @param params
     * @returns {string}
     */
    static paramToQueryString(params = {}) {
        let result = ""
        Object.keys(params).length && Object.keys(params).forEach(key => {
            if (result.length !== 0) {
                result += "&"
            }
            result += key + "=" + params[key]
        })
        return result
    }

    /**
     * 組合地址和數(shù)據(jù)
     * 比如 detail,{articleId:1} => detail?articleId=1
     * @param path
     * @param data
     * @returns {*}
     */
    static combineHash(path, data = {}) {
        if (!Object.keys(data).length) return path.replace("/", "")
        return (path + "?" + this.paramToQueryString(data)).replace("/", "")
    }
}

export default Router

說(shuō)明:這里修改了push方法,原本callback在這里調(diào)用的,但是現(xiàn)在換成在init調(diào)用。在init中監(jiān)聽(tīng)了hashchange事件,這樣就可以在hash變化的時(shí)候,需要路由配置并調(diào)用callback。而在監(jiān)聽(tīng)變化之前,我們先調(diào)用了一次,是因?yàn)槿绻覀兊谝淮芜M(jìn)入就有hash,那么就不會(huì)觸發(fā)hanshchange,所以我們需要手動(dòng)調(diào)用一遍,為了初始化第一次訪問(wèn)的頁(yè)面,這樣我們就可以通過(guò)不同的地址訪問(wèn)不同的頁(yè)面了,而整個(gè)站點(diǎn)只初始化了一次(在不使用按需加載的情況下),體驗(yàn)非常好,還要另外一種實(shí)行這里先不講,日后有空獨(dú)立出來(lái)講關(guān)于路由的東西。

0x005 將自己實(shí)現(xiàn)的路由和React集成

重構(gòu)ArticlePage

class ArticlePage extends React.Component {

    render() {
        return 

文章列表


{ ArticleService.getAll().map((article, index) => { return
this.handleClick(article)}>
{article.title}

{article.summary}


}) }
} handleClick(article) { Router.push("/detail", {articleId: article.id}) } }

重構(gòu)DetailPage

class DetailPage extends React.Component {
    render() {
        const {title, summary, detail} = ArticleService.getById(this.props.data.articleId)
        return 

{title}

{summary}


{detail}

} handleClick() { Router.push("/index") } }

重構(gòu)路由配置和渲染

const routes = [
    {
        path: "/index",
        name: "主頁(yè)",
        component: ArticlePage
    },
    {
        path: "/detail",
        name: "詳情頁(yè)",
        component: DetailPage
    }
];


Router.register(routes, (data, route) => {
    let Component = route.component
    ReactDom.render(
        ,
        document.getElementById("app")
    )
})
0x006 為React定制Router組件
在上面每調(diào)用一次Router.push,就會(huì)執(zhí)行一次ReactDom.render,并不符合React的思想,所以,需要為React定義一些組件

RouteApp組件

class RouterApp extends React.Component {
    componentDidMount(){
        Router.init()
    }
    render() {
        return {...this.props.children}
    }

}

Route組件

class Route extends React.Component {
    constructor(props) {
        super()
        this.state={
            path:props.path,
            match:"",
            data:{}
        }
    }

    componentDidMount() {
        Router.register({
            path: this.props.path
        }, (data, route) => {
            this.setState({
                match:route.path,
                data:data
            })
        })

    }

    render() {
        let Component = this.props.component
        if (this.state.path===this.state.match){
            return 
        }
        return null
    }
}

使用

class App extends React.Component {
    render() {
        return (
) } } ReactDom.render( , document.getElementById("app") )

說(shuō)明

RouterApp組件中調(diào)用了Route.init來(lái)初始化調(diào)用,然后在每個(gè)Route中注冊(cè)路由,每次路由變化的時(shí)候都會(huì)導(dǎo)致Route組件更新,從而使組件切換。

0x007 總結(jié)

路由本身是不帶有任何特殊的屬性的,在與框架集成的時(shí)候,應(yīng)該考慮框架的特點(diǎn),比如react的時(shí)候,我們可以使用reactreact-route直接結(jié)合,也可以通過(guò)使用react-route-dom來(lái)結(jié)合。

0x008 資源

源碼

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/98045.html

相關(guān)文章

  • 前端插拔式 SPA 應(yīng)用架構(gòu)實(shí)現(xiàn)方案

    摘要:插拔式應(yīng)用架構(gòu)方案和傳統(tǒng)前端架構(gòu)相比有以下幾個(gè)優(yōu)勢(shì)業(yè)務(wù)模塊分布式開(kāi)發(fā),代碼倉(cāng)庫(kù)更易管理。 showImg(https://segmentfault.com/img/remote/1460000016053325?w=2250&h=1500); 背景 隨著互聯(lián)網(wǎng)云的興起,一種將多個(gè)不同的服務(wù)集中在一個(gè)大平臺(tái)上統(tǒng)一對(duì)外開(kāi)放的概念逐漸為人熟知,越來(lái)越多與云相關(guān)或不相關(guān)的中后臺(tái)管理系統(tǒng)或企業(yè)級(jí)...

    Cciradih 評(píng)論0 收藏0
  • Router入門0x201: 從 URL 到 SPA

    摘要:的全稱是統(tǒng)一資源定位符英文,可以這么說(shuō),是一種標(biāo)準(zhǔn),而網(wǎng)址則是符合標(biāo)準(zhǔn)的一種實(shí)現(xiàn)而已。渲染器,將組件渲染到頁(yè)面上。 0x000 概述 從這一章開(kāi)始就進(jìn)入路由章節(jié)了,并不直接從如何使用react-route來(lái)講,而是從路由的概念和實(shí)現(xiàn)來(lái)講,達(dá)到知道路由的本質(zhì),而不是只知道如何使用react-route庫(kù)的目的,畢竟react-route只是一個(gè)庫(kù),是路由的一個(gè)實(shí)現(xiàn)而已,而不是路由本身。 ...

    honmaple 評(píng)論0 收藏0
  • 使用 Vue.js Flask 來(lái)構(gòu)建一個(gè)單頁(yè)的App

    摘要:我們將創(chuàng)建一個(gè)簡(jiǎn)單的,它將從到返回一個(gè)隨機(jī)數(shù)。我們來(lái)改變組件顯示隨機(jī)數(shù)在這個(gè)階段,我們只是模仿客戶端的隨機(jī)數(shù)生成過(guò)程。 在這個(gè)教程中,我們將講解如何將vue.js單頁(yè)應(yīng)用與Flask后端進(jìn)行連接。 一般來(lái)說(shuō),如果你只是想通過(guò)Flask模板使用vue.js庫(kù)也是沒(méi)有問(wèn)題的。但是,實(shí)際上是一個(gè)很明顯的問(wèn)題那就是,Jinja(模板引擎)也和Vue.js一樣采用雙大括號(hào)用于渲染,但只是一個(gè)還算...

    LiuZh 評(píng)論0 收藏0
  • Vue.js 服務(wù)端渲染業(yè)務(wù)入門實(shí)踐

    摘要:說(shuō)起,其實(shí)早在出現(xiàn)之前,網(wǎng)頁(yè)就是在服務(wù)端渲染的。沒(méi)有涉及流式渲染組件緩存對(duì)的服務(wù)端渲染有更深一步的認(rèn)識(shí),實(shí)際在生產(chǎn)環(huán)境中的應(yīng)用可能還需要考慮很多因素。選擇的服務(wù)端渲染方案,是情理之中的選擇,不是對(duì)新技術(shù)的盲目追捧,而是一切為了需要。 作者:威威(滬江前端開(kāi)發(fā)工程師)本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明作者及出處。 背景 最近, 產(chǎn)品同學(xué)一如往常笑嘻嘻的遞來(lái)需求文檔, 縱使內(nèi)心萬(wàn)般拒絕, 身體倒是很誠(chéng)實(shí)...

    miya 評(píng)論0 收藏0
  • 從頭開(kāi)始學(xué)習(xí)vue-router

    摘要:路由模塊的本質(zhì)就是建立起和頁(yè)面之間的映射關(guān)系。這時(shí)候我們可以直接利用傳值了使用來(lái)匹配路由,然后通過(guò)來(lái)傳遞參數(shù)跳轉(zhuǎn)對(duì)應(yīng)路由配置于是我們可以獲取參數(shù)六配置子路由二級(jí)路由實(shí)際生活中的應(yīng)用界面,通常由多層嵌套的組件組合而成。 一、前言 要學(xué)習(xí)vue-router就要先知道這里的路由是什么?為什么我們不能像原來(lái)一樣直接用標(biāo)簽編寫鏈接哪?vue-router如何使用?常見(jiàn)路由操作有哪些?等等這些問(wèn)...

    tommego 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<