资讯专栏INFORMATION COLUMN

从history api看主流框架的路由机制

Lyux / 3268人阅读

摘要:前端路由库的作用是改变地址栏,支持浏览器前进后退,并同步路由对应的视图,这里以及其依赖的库说一下路由机制原文地址前提首先简单介绍一下前端路由机制所依赖的事件及对应的事件对于支持新增方法的浏览器,可以通过设置来在浏览器栈中新增一条记录设置时

前端路由库的作用是改变地址栏,支持浏览器前进、后退,并同步路由对应的视图,这里以react-router及其依赖的history库说一下路由机制

原文地址

前提

首先简单介绍一下前端路由机制所依赖的pushState、popstate事件、hash及对应的hashChange事件

pushState,popstate

对于支持html5 新增pushState、replaceState方法的浏览器,可以通过设置pushState来在浏览器history栈中新增一条记录

设置pushState(),replaceState()时并不会触发popstate事件,popstate事件只在点击浏览器前进、后退按钮或调用history.back()、history.forward()等时触发

pushState()方法第一个参数可以指定一个state对象,并通过history.state或popstate事件回调中event对象获取

history.pushState(state,title,path)

console.log(history.state)

window.addEventListener("popstate",(e)=>{

    console.log(e.state)

})

location.hash hashChange

对于不支持pushState方法的浏览器,可以通过改变location.hash和借助hashChange事件来实现路由功能

window.addEventListener("hashchange",e=>{

})

location.hash="test"

对比

通过设置history.pushState(state,title,path),可以给对应路由设置一个state,这就给路由之间的数据传递提供了一种新途径,并且,state对象是保存在本地的,刷新页面依然存在,但通过hash方式实现的路由就没法使用,react-router v4版本也去除了对state的模拟

history库介绍

history库提供了三种不同的方法来创建history对象,这里的history对象是对浏览器内置window.history方法的扩展,扩展了push,go,goBack,goForward等方法,并加入了location、listen字段,并对非浏览器环境实现polyfill

createBrowserHistory()
createHashHistory()
createMemoryHistory()
react-router的路由实现(BrowserRouter和createBrowserHistory)

react-router路由实现大体过程

调用history.push跳转路由时,内部执行window.history.pushState在浏览器history栈中新增一条记录,改变url,执行组件注册的回调函数,

createBrowserHistory中注册popstate事件,用户点击浏览器前进、回退时,在popstate事件中获取当前的event.state,重新组装一个location,执行组件注册的回调函数

history库对外暴露createBrowserHistory方法,react-router中实例化createBrowserHistory方法对象,在组件中注册history.listen()回调函数,当路由有变化时,组件中匹配location,同步UI

分别来看

history.push

在react中,我们可以调用history.push(path,state)来跳转路由,实际执行的就是createBrowserHistory中的push方法

在这个方法中主要做三件事

根据传递的path,state参数创建一个location,不同于window.location,这里的location只有这些属性

   location= {
      path:
      search:
      hash:
      state:
      key
    };
    const location = createLocation(path, state, createKey(), history.location);

这个location会在组件中使用,来根据location中的值和中的path匹配,匹配成功的Route组件渲染指定的component

执行globalHistory.pushState({ key, state }, null, href);

执行Router中注册的listener

const action = "PUSH"
setState({ action, location });

const setState = nextState => {
    Object.assign(history, nextState);

    history.length = globalHistory.length;

    transitionManager.notifyListeners(history.location, history.action);
};
history中对popstate事件的注册

popstate事件触发时,可以得到event.state,createBrowserHistory中会根据这个state和当前window.location重新生成一个location对象,执行Router组件注册的listener,同步UI

const setState = nextState => {
    Object.assign(history, nextState);

    history.length = globalHistory.length;

    transitionManager.notifyListeners(history.location, history.action);
  };

const handlePop = location => {
    const action = "POP";
    setState({action,location)
}
组件

BrowserRouter组件中会实例化一个createBrowserHistory对象,传递给Router组件

class BrowserRouter extends React.Component{

    history = createHistory(this.props);

    render() {
        return ;
    }

}

在Router组件中要注册history.listen()的一个监听函数,并且保存一份子组件(Route)使用的数据

getChildContext() {
    return {
      router: {
        ...this.context.router,
        history: this.props.history,
        route: {
          location: this.props.history.location, //history中的location
          match: this.state.match
        }
      }
    };
  }
componentWillMount{
    this.unlisten = history.listen(() => {
        this.setState({
            match: this.computeMatch(history.location.pathname)
        });
    });
}

当调用history.push或触发popstate事件时,这里注册的listener都会被createBrowserHistory执行,触发setState,然后Router的子组件中匹配的会重新渲染,





在Route中有一个match状态,在父组件props发生变化时会重新计算

state = {
    match: this.computeMatch(this.props, this.context.router)
  };

componentWillReceiveProps(nextProps, nextContext) {
    this.setState({
      match: this.computeMatch(nextProps, nextContext.router)
    });
}
//computeMatch主要工作就是匹配当前组件上指定的path和当前浏览器的路径是否一致,一致就渲染组件

render() {

    if (component) return match ? React.createElement(component, props) : null;

    if (render) return match ? render(props) : null;

}
总结

总结一下,react-router的路由机制就是

借助history库,history中实现了push,go,goBack等方法,注册了popstate事件,当路由跳转时,使用浏览器内置的history api 操作 history栈

history库对外暴露的history对象提供了listen方法,组件会注册一个listener

当调用hsitory.push或popstate事件触发时,执行listener

注册的监听函数内部会setState更新状态

的子组件的componentWillReceiveProps生命周期函数中能得到Router中context,根据当前path和浏览器当前location来判断当前route是否match,匹配就渲染component

虽然本文以react-router来介绍路由机制,但主流路由库实现原理都差不多,借助pushState或hash来更新url,在对应的事件处理函数中来做视图的同步

参考

createBrowserHistory.js

locationUtil.js

BrowserRouter.js

Router.js

Route.js

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107215.html

相关文章

  • 前端路由实现与 react-router 源码分析

    摘要:回调函数将在更新时触发,回调中的起到了新的的作用。注册回调在中使用注册的回调函数,最终放在模块的回调函数数组中。 原文地址:https://github.com/joeyguo/blog/issues/2 在单页应用上,前端路由并不陌生。很多前端框架也会有独立开发或推荐配套使用的路由系统。那么,当我们在谈前端路由的时候,还可以谈些什么?本文将简要分析并实现一个的前端路由,并对 reac...

    ISherry 评论0 收藏0
  • 让前端监控数据采集更高效

    摘要:如何在新的技术背景下让前端数据采集工作更加完善高效,是本文讨论的重点。具体来说,我们对前端的数据采集具体主要分为路由切换性能资源错误日志上报路由切换等前端技术的快速发展使单页面应用盛行。 随着业务的快速发展,我们对生产环境下的问题感知能力越来越关注。作为距离用户最近的一层,前端的表现是否可靠、稳定、好用,很大程度上决定着用户对整个产品的体验和感受。因此,对于前端的监控不容忽视。 搭建一...

    Half 评论0 收藏0
  • SPA那点事

    摘要:单页面应用的出现依然存在着争议性,我们该如何看待他的两面性呢接下来小生给大家总结一下他的优缺点。单页面应用的优势无刷新体验没有了令人诟病的页面频繁刷新,同时节约浏览器资源,路由响应比较及时,提升了用户的体验。 前端猿一天不学习就没饭吃了,后端猿三天不学习仍旧有白米饭摆于桌前。IT行业的快速发展一直在推动着前端技术栈在不断地更新换代,前端的发展成了互联网时代的一个缩影。而单页面应用的发展...

    PumpkinDylan 评论0 收藏0
  • SPA那点事

    摘要:单页面应用的出现依然存在着争议性,我们该如何看待他的两面性呢接下来小生给大家总结一下他的优缺点。单页面应用的优势无刷新体验没有了令人诟病的页面频繁刷新,同时节约浏览器资源,路由响应比较及时,提升了用户的体验。 前端猿一天不学习就没饭吃了,后端猿三天不学习仍旧有白米饭摆于桌前。IT行业的快速发展一直在推动着前端技术栈在不断地更新换代,前端的发展成了互联网时代的一个缩影。而单页面应用的发展...

    Lsnsh 评论0 收藏0

发表评论

0条评论

Lyux

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<