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

前端路由库的作用是转变地点栏,支撑浏览器行进、退却,并同步路由对应的视图,这里以react-router及其依靠的history库说一下路由机制

原文地点

条件

起首简朴引见一下前端路由机制所依靠的pushState、popstate事宜、hash及对应的hashChange事宜

  1. 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)

})
  1. location.hash hashChange

关于不支撑pushState要领的浏览器,能够经由历程转变location.hash和借助hashChange事宜来完成路由功用

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

})

location.hash="test"
  1. 对照

经由历程设置history.pushState(state,title,path),能够给对应路由设置一个state,这就给路由之间的数据通报供应了一种新途径,而且,state对象是保留在当地的,革新页面依旧存在,但经由历程hash体式格局完成的路由就没法运用,react-router v4版本也去除了<HashRouter>对state的模仿

history库引见

history库供应了三种差别的要领来建立history对象,这里的history对象是对浏览器内置window.history要领的扩大,扩大了push,go,goBack,goForward等要领,并加入了location、listen字段,并对非浏览器环境完成polyfill

createBrowserHistory()
createHashHistory()
createMemoryHistory()

react-router的路由完成(BrowserRouter和createBrowserHistory)

react-router路由完成大致历程

  1. 挪用history.push跳转路由时,内部实行window.history.pushState在浏览器history栈中新增一条纪录,转变url,实行<Router></Router>组件注册的回调函数,
  2. createBrowserHistory中注册popstate事宜,用户点击浏览器行进、回退时,在popstate事宜中猎取当前的event.state,从新组装一个location,实行<Router></Router>组件注册的回调函数
  3. history库对外暴露createBrowserHistory要领,react-router中实例化createBrowserHistory要领对象,在<Router>组件中注册history.listen()回调函数,当路由有变化时,<Route>组件中婚配location,同步UI

离别来看

history.push

在react中,我们能够挪用history.push(path,state)来跳转路由,现实实行的就是createBrowserHistory中的push要领

在这个要领中重要做三件事

  1. 依据通报的path,state参数建立一个location,差别于window.location,这里的location只要这些属性
   location= {
      path:
      search:
      hash:
      state:
      key
    };
    const location = createLocation(path, state, createKey(), history.location);

这个location会在<Router><Route>组件中运用,来依据location中的值和<Route path='xxx'></Route>中的path婚配,婚配胜利的Route组件衬着指定的component

  1. 实行globalHistory.pushState({ key, state }, null, href);
  2. 实行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)
}

<Router>与<Route>组件

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

class BrowserRouter extends React.Component{

    history = createHistory(this.props);

    render() {
        return <Router history={this.history} children={this.props.children} />;
    }

}

在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>会从新衬着,


<Router>
<Route path='/path1' compoent={}>
<Route path='/path2' compoent={}>
<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的路由机制就是

  1. 借助history库,history中完成了push,go,goBack等要领,注册了popstate事宜,当路由跳转时,运用浏览器内置的history api 操纵 history栈
  2. history库对外暴露的history对象供应了listen要领,<Router></Router>组件会注册一个listener
  3. 当挪用hsitory.push或popstate事宜触发时,实行listener
  4. <Router></Router>注册的监听函数内部会setState更新状况
  5. <Router></Router>的子组件<Route>的componentWillReceiveProps性命周期函数中能获得Router中context,依据当前path和浏览器当前location来推断当前route是不是match,婚配就衬着component

虽然本文以react-router来引见路由机制,但主流路由库完成原理都差不多,借助pushState或hash来更新url,在对应的事宜处置惩罚函数中来做视图的同步

参考

    原文作者:对对对就是这
    原文地址: https://segmentfault.com/a/1190000013126134
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞