前端两种路由方案
- 无hash
通过h5的pushState、replaceState、go、forward、back配合onpopstate来处理。注意pushState无法进入popstate事件。pushState兼容到ie10。
pushState是如何处理路由的?是实现一个onpushstate事件,然后重写pushstate方法,在每次调用pushState的时候该事件。 - 有hash
传统方式,改变window.location.hash配合onhashchange事件来渲染页面内容。onhashchange兼容ie8及以上,ie8以下用setInterval监测hash。
相关正则
-
(?:)
表示当前捕获组会匹配但是不会被拿到匹配结果中。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。 -
yyy(?=xxx)
表示匹配后面为xxx的yyy。名字叫前瞻。 -
yyy(?!xxx)
表示匹配后面不为xxx的yyy。名字叫负前瞻。
path-to-regexp工具库
这个库很多前端框架都在用,来匹配路由。比如vue
、react
、koa
、express
。
let pathToRegExp = require('path-to-regexp');
let regx = pathToRegExp('/home',[],{end:true});
console.log(regx);// /^\/home\/?$/i /^\/home(?:\/)?$/i
匹配两种情况
-
/home
开头,以/
结尾。 -
/home
开头并结尾。
let regx2 = pathToRegExp('/home',[],{end:false});
console.log(regx2);// /^\/home\/?(?=\/|$)/i /^\/home(?:\/(?=$))?(?=\/|$)/i
匹配三种情况
-
/home
开头,以/
结尾。 -
/home
开头并结尾。 -
/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
当用户输入了一个错误的路由,或者点击一级路由的时候,需要显示一个默认的子路由,这时候怎么办呢?
需要Switch
和Redirect
组件来配合实现。
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前调用一个函数(监听函数),这样就模拟了事件触发。