前端两种路由方案

  1. 无hash
    通过h5的pushState、replaceState、go、forward、back配合onpopstate来处理。注意pushState无法进入popstate事件。pushState兼容到ie10。
    pushState是如何处理路由的?是实现一个onpushstate事件,然后重写pushstate方法,在每次调用pushState的时候该事件。
  2. 有hash
    传统方式,改变window.location.hash配合onhashchange事件来渲染页面内容。onhashchange兼容ie8及以上,ie8以下用setInterval监测hash。

相关正则

  1. (?:)表示当前捕获组会匹配但是不会被拿到匹配结果中。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
  2. yyy(?=xxx)表示匹配后面为xxx的yyy。名字叫前瞻。
  3. yyy(?!xxx)表示匹配后面不为xxx的yyy。名字叫负前瞻。

path-to-regexp工具库

这个库很多前端框架都在用,来匹配路由。比如vuereactkoaexpress

let pathToRegExp = require('path-to-regexp');
let regx = pathToRegExp('/home',[],{end:true});
console.log(regx);//   /^\/home\/?$/i /^\/home(?:\/)?$/i
image.png

匹配两种情况

  1. /home开头,以/结尾。
  2. /home开头并结尾。
let regx2 = pathToRegExp('/home',[],{end:false});
console.log(regx2);//   /^\/home\/?(?=\/|$)/i /^\/home(?:\/(?=$))?(?=\/|$)/i
image.png-w150

匹配三种情况

  1. /home开头,以/结尾。
  2. /home开头并结尾。
  3. /home开头,字母e后面是/的,\后面任意个数字符。

keys

let keys =  []
let regx2 = pathToRegExp('/home/:id/:name',keys,{end:false});
const [url, ...groups] = path.match(regx2)
console.log(a)

这个keys比较重要,用来拿到对应的分组名id和name,result中groups就可以一一对应起来,作为routerContext中match.params的值。

Router.js(HashRouter、BrowserRouter)

这个类组件目的是为了拿到window.location的信息,并且进行监听对应hashchange事件。更新路由信息。通过react.context api使得全局都可以访问。

Route.js

这个类组件的目的是根据传进来的参数来渲染组件,比如path(匹配哪个路由)、exact(是否精确)、component(渲染什么组件)。

React-Router route渲染对应的组件可以拿到的props

image.png

当用户输入了一个错误的路由,或者点击一级路由的时候,需要显示一个默认的子路由,这时候怎么办呢?

需要SwitchRedirect组件来配合实现。
Switch组件依此遍历自己的所有children,如果匹配,则渲染对应route,Redirect不需要传path,在Switch组件中,path默认值为'/',且exact为false。如果其他都不匹配,则渲染Redirect to属性对应组件。

当希望实现是否登陆校验,跳转登陆页面,路由该怎么写呢?

<Route path='/' render={(props) => (is_logined ? <Dashboard {...props} /> : <Redirect to="/login"/>)}/>

在Router组件内可以判断,props.render是否是个函数,如果是个函数,则执行并拿到对应组件进行渲染。

当希望实现是否登陆校验,跳转登陆页面,路由该怎么写呢?

<Route path='/' render={(props) => (is_logined ? <Dashboard {...props} /> : <Redirect to="/login"/>)}/>

在Router组件内可以判断,props.render是否是个函数,如果是个函数,则执行并拿到对应组件进行渲染。

withRouter是什么?

是个高阶组件,用该组件包裹后可以拿到路由参数。内部实现就是用的react.context。注意还能拿到被包装组件到ref。
下面这段话是在幕课搜到的。

其次withRouter是专门用来处理数据更新问题的。在使用一些redux的的connect()或者mobx的inject()的组件中,如果依赖于路由的更新要重新渲染,会出现路由更新了但是组件没有重新渲染的情况。这是因为redux和mobx的这些连接方法会修改组件的shouldComponentUpdate

在使用withRouter解决更新问题的时候,一定要保证withRouter在最外层,比如withRouter(connect(Component))

Prompt的实现思路?

Prompt常用于用户大量输入文本,不消息按错跳出页面的情况。
在组件内使用

<Prompt
   when={isBlocking}
   message={location=>`你确定要跳转到${location.pathname}吗?`}
/>

isBlocking是当前组件自己setState的值,每次改变后重新渲染Prompt组件,在Prompt组件内调用ReactRouterContext中history.block方法。改变当前是否需要提示用户的状态(就是个存放在HashRouter或者BrowserRoot中的变量)。然后在监听hashchange的地方判断是否需要提醒用户。

BrowserRouter的实现思路?

和HashRouter没有区别,唯一区别是,hashRouter可以直接监听hashchange事件,而h5的pushSate方法没有原生事件可以监听,需要自己重写pushstate,就是在调用原生pushstate前调用一个函数(监听函数),这样就模拟了事件触发。

参考

阮一峰#
两种路由介绍
minrouter
jquery.pjax.js
pjax相关文章
history.js

收藏