• react-router的原理


    1、hash的方式

    以 hash 形式(也可以使用 History API 来处理)为例,当 url 的 hash 发生变化时,触发 hashchange 注册的回调,回调中去进行不同的操作,进行不同的内容的展示

    function Router() {
        this.routes = {};
        this.currentUrl = '';
    }
    Router.prototype.route = function(path, callback) {
        this.routes[path] = callback || function(){};
    };
    Router.prototype.refresh = function() {
        this.currentUrl = location.hash.slice(1) || '/';
        this.routes[this.currentUrl]();
    };
    Router.prototype.init = function() {
        window.addEventListener('load', this.refresh.bind(this), false);
        window.addEventListener('hashchange', this.refresh.bind(this), false);
    }
    window.Router = new Router();
    window.Router.init();
    
    

    link的实现

    可以从 react-router/ Link 中看到,对该组件的点击事件进行了阻止了浏览器的默认跳转行为,而改用 history 模块的 pushState 方法去触发 url 更新。

    为什么React组件会更新

    给history注册了lisetner事件,也就是里面的setState函数;

    history.push(pathname,state)函数执行,这里会执行注册的listener函数

    listerer中的React的setState会执行;

    React组件更新;

    其实无论是react-router. react-redux. 能够使组件更新的根本原因,还是最后出发了setState函数;

    对于react-router,其实是对history原生对象的封装,重新封装了push函数,使得我们在push函数执行的时候,可以触发在Router组件中组件装载之前,执行了history.listener函数,该函数的主要作用就是给listeners数组添加监听函数,每次执行history.push的时候,都会执行listenrs数组中添加的listener,这里的listener就是传入的箭头函数,功能是执行了Router组件的setState函数,从《Router Switch Route源码解析》文章中,可以看出来,Router执行了setState之后,会将当前url地址栏对应的url传递下去,当Route组件匹配到该地址栏的时候,就会渲染该组件,如果匹配不到,Route组件就返回null;

    componentWillMount() {
      const { children, history } = this.props
    
      invariant(
        children == null || React.Children.count(children) === 1,
        'A <Router> may have only one child element'
      )
    
      // Do this here so we can setState when a <Redirect> changes the
      // location in componentWillMount. This happens e.g. when doing
      // server rendering using a <StaticRouter>.
      //这里执行history.listen()方法;传入一个函数;箭头函数的this指的是父级的作用域中的this值;
      this.unlisten = history.listen(() => {
        this.setState({
          match: this.computeMatch(history.location.pathname)
        })
      })
    }
    

    1、react-router依赖基础 - history

    history是一个独立的第三方js库,可以用来兼容在不同浏览器、不同环境下对历史记录的管理,拥有统一的API。具体来说里面的history分为三类:

    • 老浏览器的history: 主要通过hash来实现,对应createHashHistory

    • 高版本浏览器: 通过html5里面的history,对应createBrowserHistory

    • node环境下: 主要存储在memeory里面,对应createMemoryHistory

    • createBrowserHistory: 利用HTML5里面的history

    • createHashHistory: 通过hash来存储在不同状态下的history信息

    • createMemoryHistory: 在内存中进行历史记录的存储

    执行URL前进

    • createBrowserHistory: pushState、replaceState
    • createHashHistory: location.hash=*** location.replace()
    • createMemoryHistory: 在内存中进行历史记录的存储

    检测URL回退

    • createBrowserHistory: popstate
    • createHashHistory: hashchange
    • createMemoryHistory: 因为是在内存中操作

    2、react-router的基本原理

    框架去拦截浏览器跳转,自己去同步UI组件

    其中在react-router中,URL对应Location对象,而UI是由react components来决定的,这样就转变成location与components之间的同步问题

    image

    通过router声明了一份含有 path to component 的各个映射的路由表。
    react-router 还提供的 Link 组件(如下),作为提供更新 url 的途径,触发 Link 后最终将通过如上面定义的路由表进行匹配,并拿到对应的 component 及 state 进行 render 渲染页面。

    3、从点击 Link 到 render 对应 component ,路由中发生了什么

    Router 在 react component 生命周期之组件被挂载前 componentWillMount 中使用 this.history.listen 去注册了 url 更新的回调函数。回调函数将在 url 更新时触发,回调中的 setState 起到 render 了新的 component 的作用。

    Router.prototype.componentWillMount = function componentWillMount() {
        // .. 省略其他
        var createHistory = this.props.history;
     
        this.history = _useRoutes2['default'](createHistory)({
          routes: _RouteUtils.createRoutes(routes || children),
          parseQueryString: parseQueryString,
          stringifyQuery: stringifyQuery
        });
     
        this._unlisten = this.history.listen(function (error, state) {
            _this.setState(state, _this.props.onUpdate);
        });
      };
    

    上面的 _useRoutes2 对 history 操作便是对其做一层包装,所以调用的 this.history 实际为包装以后的对象,该对象含有 _useRoutes2 中的 listen 方法,如下

    function listen(listener) {
          return history.listen(function (location) {
              // .. 省略其他
              match(location, function (error, redirectLocation, nextState) {
                listener(null, nextState);
              });
          });
    }
    

    可看到,上面代码中,主要分为两部分

    1. 使用了 history 模块的 listen 注册了一个含有 setState 的回调函数(这样就能使用 history 模块中的机制)
    2. 回调中的 match 方法为 react-router 所特有,match 函数根据当前 location 以及前面写的 Route 路由表匹配出对应的路由子集得到新的路由状态值 state,具体实现可见 react-router/matchRoutes ,再根据 state 得到对应的 component ,最终执行了 match 中的回调 listener(null, nextState) ,即执行了 Router 中的监听回调(setState),从而更新了展示。
    如何触发监听的回调函数的执行?

    url 更新主要有两种方式:简单的 hash 更新或使用 history api 进行地址更新。在 react-router 中,其提供了 Link 组件,该组件能在 render 中使用,最终会表现为 a 标签,并将 Link 中的各个参数组合放它的 href 属性中。可以从 react-router/ Link 中看到,对该组件的点击事件进行了阻止了浏览器的默认跳转行为,而改用 history 模块的 pushState 方法去触发 url 更新

    可以将以上 react-router 的整个包装闭环总结为
    1. 回调函数:含有能够更新 react UI 的 react setState 方法。
    2. 注册回调:在 Router componentWillMount 中使用 history.listen 注册的回调函数,最终放在 history 模块的 回调函数数组 changeListeners 中。
    3. 触发回调:Link 点击触发 history 中回调函数数组 changeListeners 的执行,从而触发原来 listen 中的 setState 方法,更新了页面

    至于前进与后退的实现,是通过监听 popstate 以及 hashchange 的事件,当前进或后退 url 更新时,触发这两个事件的回调函数,回调的执行方式 Link 大致相同,最终同样更新了 UI ,这里就不再说明。

    react-router 主要是利用底层 history 模块的机制,通过结合 react 的架构机制做一层包装.

    router3的按需加载方式

    const about = (location, cb) => {
        require.ensure([], require => {
            cb(null, require('../Component/about').default)
        },'about')
    }
    
    //配置route
    <Route path="helpCenter" getComponent={about} />
    
  • 相关阅读:
    SQL Server 跨服务器 存储过程传字符串数组不同数据库之间复制表的数据
    SQL Server 跨服务器 存储过程不同数据库之间复制表的数据
    SQL Server 跨服务器 存储过程不同数据库之间复制表的数据
    SQL Server跨服务器查询
    抓包自定义过滤器需加%XXXX%,如%third_play%
    svn 清理命令失败
    通过windug判断某个模块导致程序不能退出。
    如果判断不出是不是某个模块出的问题,删除此模块测试。
    读配置前把vector清掉
    DNS服务器的原理
  • 原文地址:https://www.cnblogs.com/chenjinxinlove/p/8467744.html
Copyright © 2020-2023  润新知