乐趣区

关于前端:SPA-路由三部曲之核心原理

为了配合单页面 Web 利用疾速倒退的节奏,近几年,各类前端组件化技术栈层出不穷。通过一直的版本迭代 React、Vue 怀才不遇,成为当下最受欢迎的两大技术栈。

仅 7 个月的工夫,两个技术栈的下载量就冲破了百万,React 甚至冲破了千万。不论是现下风行的 React、Vue,还是红极一时的 Angular、Ember,只有是单页面 Web 利用,都离不开前端路由的配合。如果把单页面 Web 利用比作一间房,每个页面对应房子中的各个房间,那么路由就是房间的门,不论房间装璜的有多丑陋,没有门,也无奈展现在用户眼前,路由在单页面 Web 利用的位置也就显而易见了。

为了能更具体的介绍前端路由,小编将从三个层面,由浅入深,一步一步的率领大家摸索前端路由的实现原理。首先通过《SPA 路由三部曲之外围原理》理解前端路由的外围常识,紧接着《SPA 路由三部曲之 MyVueRouter 实际》将率领大家实现属于本人的 vue-router,最初《SPA 路由三部曲之 VueRouter 源码解析》将挑战自我,深度解析 vue-router 源码。《SPA 路由三部曲之外围原理》将从端路由的前世今生、外围原理解析、vue-router 与 react-router 利用比照三局部对前端路由进行初步理解。

前端路由前世今生

前端路由倒退到明天,经验了后端路由、前后端路由过渡、前端路由的过程,如果你对前端路由的了解还是懵懵懂懂,那有必要理解一下它的倒退过程。

后端路由

路由这个概念最先是在后端呈现的,Web 开发还在「刀耕火种」年代时,始终是后端路由占据主导地位,页面渲染齐全依赖服务器。

在最开始的时候,HTML、CSS、JavaScript 的文件以及数据载体 json(xml) 等文件都是放到后端服务器目录下的,并且这些文件彼此是没有分割的,想要扭转网站的布局,可能会改上百个 HTML,繁琐且毫无技术含量。起初聪慧的工程师就将雷同的 HTML 整顿成模板,进行复用,胜利缩小了前端的工作量。前端工程师开始用模板语言代替手写 HTML,后端服务器目录的文件也变成了不同的模板文件。

这个期间,不论 Web 后端是什么语言的框架,都会有一个专门开辟出来的路由模块或者路由区域,用来匹配用户给出的 URL 地址,以及一些表单提交、页面申请地址。用户进行页面切换时,浏览器发送不同的 URL 申请,服务器接管到浏览器的申请时,通过解析不同的 URL 地址进行后端路由匹配,将模板拼接好后将之返回给前端残缺的 HTML,浏览器拿到这个 HTML 文件后间接解析展现了,也就是所谓的服务端渲染。

服务端渲染页面,后端有残缺的 HTML 页面,爬虫更容易获取信息,有利于 SEO 优化。对于客户端的资源占用更少,尤其是挪动端,能够更省电。

过渡

当前端路由为根底,开发的 Web 利用,都会存在一个弊病。每跳转到不同的 URL,都是从新拜访服务端,服务器拼接造成残缺的 HTML,返回到浏览器,浏览器进行页面渲染。甚至浏览器的后退、后退键都会从新拜访服务器,没有正当地利用缓存。

随着前端页面复杂性越来越高,性能越来越欠缺,后端服务器目录下的代码文件会越来越多,耦合性也越来越重大。不仅加大服务器的压力,也不利于良好的用户体验,代码保护。受限于以 JavaScript 为代表的前端技术尚未崛起,这个痛点成了程序员的最大难题。

直到 1998 年,微软的 Outloook Web App 团队提出 Ajax 的基本概念(XMLHttpRequest 的前身),置信大家对这个技术曾经十分相熟了,浏览器实现异步加载的一种技术计划,并在 IE5 通过 ActiveX 来实现了这项技术。有了 Ajax 后,页面操作就不必每次都刷新页面,体验带来了极大的晋升。

2005 年 Google Map 的公布让 Ajax 这项技术发扬光大,向人们展现了它真正的魅力,让其不仅仅局限于简略的数据和页面交互,也为起初异步交互体验形式的凋敝倒退奠定了根底。2008 年,Google V8 引擎公布,JavaScript 随之崛起,前端工程师开始借鉴后端模板思维,单页面利用就此诞生。2009 年,Google 公布 Angularjs 将 MVVM 及单页面利用发扬光大,由衷的拜服 Google 的弱小。

单页利用不仅在页面交互是无刷新的,连页面跳转都是无刷新的,为了配合实现单页面利用跳转,前端路由孕育而生。

前端路由

前端路由相较于后端路由的一个特点就是页面在不齐全刷新的状况下进行视图的切换。页面 URL 变了,然而并没有从新加载,让用户体验更靠近原生 app。

前端路由的衰亡,使得页面渲染由服务器渲染变成了前端渲染。为什么这么说呢!申请一个 URL 地址时,服务器不须要拼接模板,只需返回一个 HTML 即可,个别浏览器拿到的 HTML 是这样的:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Demo</title>
  <link href="app.css" rel="stylesheet"/>
</head>
<body>
  <div id="app"></div>
  <script type="text/javascript" src="app.js"></script>
</body>
</html>

这里空荡荡的只有一个 <div id=”app”></div>,以及一系列的 js 文件,所以说这个 HTML 是不残缺的。咱们看到的页面是通过这一系列的 js 渲染进去的,也就是前端渲染。前端渲染通过客户端的算力来解决页面的构建,很大水平上缓解了服务端的压力。

单页面开发是趋势,但也不能避重就轻,疏忽前端渲染的毛病。因为服务器没有保留残缺的 HTML,通过 js 进行动静 DOM 拼接,须要消耗额定的工夫,不如服务端渲染速度快,也不利于 SEO 优化。所以说,理论开发中,不能自觉抉择渲染形式,肯定要基于业务场景。对于没有简单交互,SEO 要求严格的网站,服务器渲染也是正确的抉择。

外围原理解析

路由形容了 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变动引起 UI 更新(无需刷新页面)。前端路由最次要的展现形式有 2 种:

  • 带有 hash 的前端路由:地址栏 URL 中有 #,即 hash 值,不难看,但兼容性高。
  • 不带 hash 的前端路由:地址栏 URL 中没有 #,难看,但局部浏览器不反对,还须要后端服务器反对。

在 vue-router 和 react-router 中,这两种展现模式,被定义成两种模式,即 Hash 模式与 History 模式。前端路由实现原理很简略,实质上就是检测 URL 的变动,截获 URL 地址,通过解析、匹配路由规定实现 UI 更新。当初就跟着小编一起来揭开它神秘的面纱吧!

Hash

一个残缺的 URL 包含:协定、域名、端口、虚拟目录、文件名、参数、锚。

hash 值指的是 URL 地址中的锚局部,也就是 # 前面的局部。hash 也称作锚点,是用来做页面定位的,与 hash 值对应的 DOM id 显示在可视区内。在 HTML5 的 history 新个性呈现前,根本都是应用监听 hash 值来实现前端路由的。hash 值更新有以下几个特点:

  • hash 值是网页的标记位,HTTP 申请不蕴含锚局部,对后端无影响
  • 因为 HTTP 申请不蕴含锚局部,所以 hash 值扭转时,不触发网页重载
  • 扭转 hash 值会扭转浏览器的历史记录
  • 扭转 hash 值会触发 window.onhashchange() 事件

而扭转 hash 值的形式有 3 种:

  • a 标签使锚点值变动,例:<a href=’#/home’></a>
  • 通过设置 window.location.hash 的值
  • 浏览器后退键(history.forword())、后退键(history.back())

综上所述,这 3 种扭转 hash 值的形式,并不会导致浏览器向服务器发送申请,浏览器不发出请求,也就不会刷新页面。hash 值扭转,触发全局 window 对象上的 hashchange 事件。所以 hash 模式路由就是利用 hashchange 事件监听 URL 的变动,从而进行 DOM 操作来模仿页面跳转。

History

在解说 History 之前,大家先思考一个问题,点击浏览器左上角的回退按钮为什么能回到之前的浏览记录,点击后退按钮就能回到回退之前的浏览记录?这是因为浏览器有一个相似栈的历史记录,遵循先进后出的规定。URL 的每次扭转,包含 hash 值的变动都会在浏览器中造成一条历史记录。window 对象通过 history 对象提供对览器历史记录的拜访能力。

  • history.length
    出于平安思考,History 对象不容许未受权代码拜访历史记录中其它页面的 URLs,但能够通过 history.length 拜访历史记录对象的长度。
  • history.back()
    回退到上一个历史记录,同浏览器后退键
  • history.forward()
    后退到下一个历史记录,同浏览器后退键
  • history.go(n)
    跳转到相应的拜访记录;若 n > 0,则后退,若 n < 0,则后退,若 n = 0,则刷新以后页面

为了配合单页面的倒退,HTML5 对 History API 新增的两个办法:pushState()、replaceState(),均具备操纵浏览器历史记录的能力。

history.pushState(state, title, URL)

pushState 共接管 3 个参数:

  • state:用于存储该 URL 对应的状态对象,能够通过 history.state 获取
  • title:题目,目前浏览器并不反对
  • URL:定义新的历史 URL 记录,须要留神,新的 URL 必须与以后 URL 同源,不能跨域

pushState 函数会向浏览器的历史记录中增加一条,history.length 的值会 +1,以后浏览器的 URL 变成了新的 URL。须要留神的是:仅仅将浏览器的 URL 变成了新的 URL,页面不会加载、刷新。简略看个例子:

通过 history.pushState({tag: "cart"}, "","cart.html"),将 /home.html 变成 /cart.html 时,只有 URL 产生了扭转,cart.html 页面并没有加载,甚至浏览器都不会去检测该门路是不是存在。这也就是证实了,pushState 在不刷新页面的状况下批改浏览器 URL 链接,单页面路由的实现也就是利用了这一个个性。

细心地童鞋应该发现了,通过 pushState 设置新的 URL 的办法与通过 window.location='#cart' 设置 hash 值扭转 URL 的办法有相似之处:URL 都产生了扭转,在以后文档内都创立并激活了新的历史记录条目,但页面均没有从新渲染,浏览器没有发动申请。那前者的劣势又是什么呢?

  • 新的 URL 能够是任意同源的 URL,而 window.location,只能通过扭转 hash 值能力保障留在以后 document 中,浏览器不发动申请
  • 新的 URL 能够是以后 URL,不扭转,就能够创立一条新的历史记录项,而 window.location 必须设置不同的 hash 值,能力创立。如果以后 URL 为 /home.html#foo,应用 window.location 设置 hash 时,hash
    值必须不能是 #foo,能力创立新的历史记录
  • 能够通过 state 参数在新的历史记录项中增加任何数据,而通过 window.location 扭转 hash 的形式,只能将相干的数据转成一个很短的字符串,以 query 的模式放到 hash 值前面
  • 尽管 title 参数当初还不能被所有的浏览器反对,前端倒退这么快,谁能说的准之后产生的事件呢!

history.replaceState(state, title, URL)

replaceState 的应用与 pushState 十分类似,都是扭转以后的 URL,页面不刷新。区别在于 replaceState 是批改了以后的历史记录项而不是新建一个,history.length 的值放弃不变。

从下面的动画,咱们就能够晓得,通过 history.replaceState({tag: "cart"}, "","cart.html") 扭转 URL 之前,history 的历史记录为 /classify.html/home.html,URL 扭转之后,点击浏览器后退键,间接回到了 /classify.html,跳过了 /home.html。也就证实了 replaceState 将历史记录中的 /home.html 批改为 /cart.html,而不是新建 /cart.html

window.onpopstate()

通过 a 标签或者 window.location 进行页面跳转时,都会触发 window.onload 事件,页面实现渲染。点击浏览器的后退键或后退键,依据浏览器的不同机制,也会从新加载(Chrome 浏览器),或保留之前的页面(Safari 浏览器)。而对于通过 history.pushState() 或 history.replaceState() 扭转的历史记录,点击浏览器的后退键或后退键页面是没有反馈的,那该如何管制页面渲染呢?为了配合 history.pushState() 或 history.replaceState(),HTML5 还新增了一个事件,用于监听 URL 历史记录扭转:window.onpopstate()。

官网对于 window.onpopstate() 事件的形容是这样的:

每当处于激活状态的历史记录条目发生变化时,popstate 事件就会在对应 window 对象上触发。如果以后处于激活状态的历史记录条目是由 history.pushState() 办法创立, 或者由 history.replaceState() 办法批改过的, 则 popstate 事件对象的 state 属性蕴含了这个历史记录条目标 state 对象的一个拷贝。调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。popstate 事件只会在浏览器某些行为下触发, 比方点击后退、后退按钮 (或者在 JavaScript 中调用 history.back()、history.forward()、history.go() 办法),此外,a 标签的锚点也会触发该事件。

第一次读到这段话的时候似懂非懂,思考了很久,也做了很多的例子,发现其中的坑很多,这些坑次要是因为每个浏览器机制不同。官网文档对 window.onpopstate() 的形容很少,也有很多不明确的中央,依据本人的测试,来拆解一下官网形容,如果有不对的,还心愿大家指出。

1. 每当处于激活状态的历史记录条目发生变化时,popstate 事件就会在对应 window 对象上触发。

对这句话的了解是,在浏览器中输出一个 URL,使其处于激活状态,不论通过哪种形式,只有 URL 扭转,popstate 就会触发。但理论状况却是:只有通过 pushState 或 replaceState 扭转的 URL,在点击浏览器后退键的时候才会触发,如果是通过 a 标签或 window.location 实现 URL 扭转(不是扭转锚点)页面跳转,在点击浏览器回退键的时候,并不会触发。对这种状况,我有两个猜想:

  • popstate 事件是异步函数。因为通过 a 标签或 window.location 实现 URL 扭转时,以后页面卸载,新的页面加载。因为 popstate 事件是异步的,在页面卸载之前并没来得及加载。
  • 只有触发新增的 pushState 与 replaceState 扭转的历史记录条目,才会触发 popstate 事件,毕竟 popstate 事件的呈现是为了配合 pushState 与 replaceState。

查阅了很多材料,这两个猜想没有失去证实,但有一点能够必定,想要监听到 popstate 事件,必须是应用 pushState 与 replaceState 扭转的历史记录。

2. 调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件,popstate 事件只会浏览器的某些行为下触发。

因为各个浏览器的机制不同,测试后果也是不同的。咱们先在 Chrome 浏览器下做个测试:
home.html

<div>
  <h3>home html</h3>
  <div id="btn" class="btn"> 跳转至 cart.html</div>
  <a href="classify.html"> a 标签跳转至 classify.html</a>
</div>
<script>
  document.getElementById('btn').addEventListener('click', function(){history.replaceState({ tag: "cart"}, "","cart.html")
   }, false); 
   window.addEventListener('popstate', ()=>{console.log('popstate home 跳转')
   })
</script>

咱们进行这样的操作:以后 URL 为 /home.html,通过 history.pushState({tag: "cart"}, "","cart.html") 将以后 URL 变成了 /cart.html。这个过程中,home.html 中的 popstate 事件的确没有触发。此时点击浏览器后退键,URL 变回了/home.htmlhome.html 中的 popstate 事件触发了。

那如果咱们跳出 /home.html 的 document 呢?通过 history.pushState({tag: "cart"}, "","cart.html") 将以后 URL 变成了 /cart.html 后,点击 a 标签将 URL 变为 /classify.html

执行到这里,咱们须要明确一点:a 标签扭转 URL,浏览器会从新发动申请,页面产生了跳转,window 对象也产生了扭转。popstate 官网文档第一句指出:popstate 事件是在对应 window 对象上触发。此时,咱们点击浏览器后退键,URL 变成 /cart.html,执行 /cart.html 中的 load 事件,页面加载。再次点击浏览器后退键,URL 变为 /home.html/cart.html 中的 popstate 事件触发,页面未渲染。

popstate 事件尽管触发了,然而是 cart.html 页面中定义的 popstate 事件,并不是 home.html 的事件。并且同样的浏览器回退键操作,在 Safari 浏览器的展现是这样的:

在浏览器回退时,Safari 浏览器与 Chrome 浏览器对于页面的加载呈现了差别。classify.html 回退到 cart.html,URL 变成了 /cart.html, 但触发了 home.html 中的 popstate 事件,持续回退,URL 变成了 /home.html, 仍然触发了 home.html 中 popstate 事件。

Chrome 浏览器与 Safari 浏览器差别的产生与浏览器对 popstate 事件处理有关系。至于浏览器外部是怎么解决的,小编也没有钻研分明。尽管 Chrome 浏览器与 Safari 浏览器对于 popstate 事件的解决形式不一样,然而 URL 的回退门路是统一的,完全符合历史记录后进先出的规定。

在理论开发中,这种状况也是存在的:URL 由 /home.html/cart.html 的扭转,就相似单页面开发中的跳转。若此时在 cart.html 中,须要应用 pushState 跳出单页面,进入登录页,用户在登录页点击浏览器回退,或挪动端手势返回。上述情况就会呈现,Chrome 浏览器与 Safari 浏览器渲染页面不统一。

popstate 官网形容是“popstate 事件会在 对应 window 对象 上触发”,留神是 对应 window 对象,这个概念就比拟含糊了,指的是触发 pushState 的 window 对象,还是 pushState 新定义的 window 对象。依据咱们上述的测试,都有可能触发 popstate 事件。所以童鞋们,在遇到下面状况时,肯定不要遗记在相干的两个页面中都要做 popstate 监听解决。

3.a 标签的锚点也能够触发 popstate 事件的办法

与 pushState 和 replaceState 不同,a 标签锚点的变动会立刻触发 popstate 事件。这里咱们扩大一下思路,a 标签做的事件就是扭转了 hash 值,那通过 window.location 扭转 hash 值是不是也是能立刻触发 popstate。答案是必定的,也会立刻触发 popstate。

通过 hash 大节的理解,hash 值的扭转会触发 hashchange 事件,所以,hash 值的扭转会同时触发 popstate 事件与 hashchange 事件,但如果扭转的 hash 值与以后 hash 值一样的话,hashchange 事件不触发,popstate 事件触发。之前咱们说过,window.location 设置的 hash 值必须与以后 hash 值不一样能力新建一条历史记录,而 pushState 却能够。

联合上述,在浏览器反对 pushState 的状况下,hash 模式路由也能够应用 pushState、replaceState 和 popstate 实现。pushstate 扭转 hash 值,进行跳转,popstate 监听 hash 值的变动。小小的剧透,vue-router 中不论是 hash 模式,还是 history 模式,只有浏览器反对 history 的新个性,应用的都是 history 的新个性进行跳转。

前端路由利用

其实 history 和 hash 都是浏览器自有的个性,单页面路由只是利用了这些个性。在不跳出以后 document 的状况下,除了 history 本身的兼容性之外,各个浏览器都不会存在差别,而单页面开发就是在一个 document 中实现所有的交互,这两者的完满联合,将前端开发晋升到了一个新的高度。

vue-router 和 react-router 是当初最风行的路由状态管理工具。两者实现原理尽管是统一的,但因为所依赖的技术栈不同,应用形式也略有不同。在 react 技术栈开发时,大部分的童鞋还是喜爱应用 react-router-dom,它基于 react-router,退出了在浏览器运行环境下的一些性能。

注入形式

1. vue-router

vue-router 能够在 vue 我的项目中全局应用,vue.use() 功不可没。通过 vue.use(),向 VueRouter 对象注入了 Vue 实例,也就是根组件。根组件将 VueRouter 实例一层一层的向下传递,让每个渲染的子组件领有路由性能。

import VueRouter from 'vue-router'
const routes = [{ path: '/',name: 'home',component: Home,meta:{title:'首页'} }
]
const router = new myRouter({
    mode:'history',
    routes
})
Vue.use(VueRouter)

2. react-router-dom

react-router 的注入形式是在组件树顶层放一个 Router 组件,而后在组件树中散落着很多 Route 组件,顶层的 Router 组件负责剖析监听 URL 的变动,在其上面的 Route 组件渲染对应的组件。在残缺的单页面我的项目中,应用 Router 组件将根组件包裹,就能实现保障失常的路由跳转。

import {BrowserRouter as Router, Route} from 'react-router-dom';
class App extends Component {render() {
        return (
            <Router>
                <Route path='/' exact component={Home}></Route>
            </Router>
        )
    }
}

根底组件

1. vue-router 提供的组件次要有 <outer-link/> 和 <router-view/>

  • <router-link/> 能够操作 DOM 间接进行跳转,定义点击后导航到哪个门路下;对应的组件内容渲染到 <router-view/> 中。

2. react-router-dom 罕用到的是 <BrowserRouter/>、<HashRouter/>、<Route/>、<Link/>、<Switch/>

  • <BrowserRouter/>、<HashRouter/> 组件看名字就晓得,用于辨别路由模式,并且保障 React 我的项目具备页面跳转能力。
  • <Link /> 组件与 vue-router 中的 <router-link/> 组件相似,定义点击后的指标导航门路,对应的组件内容通过 <Route /> 进行渲染。
  • <Switch/> 用来将 react-router 由包容性路由转换为排他性路由,每次只有匹配胜利就不会持续向下匹配。vue-router 属于排他性路由。

路由模式

1. vue-router 次要分为 hash 和 history 两种模式。在 new VueRouter() 时,通过配置路由选项 mode 实现。

  • Hash 模式:地址栏 URL 中有 #。vue-router 优先判断浏览器是否反对 pushState,若反对,则通过 pushState 扭转 hash 值,进行指标路由匹配,渲染组件,popstate 监听浏览器操作,实现导航性能,若不反对,应用 location.hash 设置 hash 值,hashchange 监听 URL 变动实现路由导航。
  • History 模式:地址栏 URL 中没有 #。与 Hash 模式实现导航的思路是一样的。不同的是,vue-router 提供了 fallback 配置,当浏览器不反对 history.pushState 管制路由是否应该回退到 hash 模式。默认值为 true。

    网上材料对 Hash 路由模式的原理剖析大都是通过 location.hash 联合 hashchange 实现,与上述形容的 hash 路由模式的实现形式不同,这也是小编最近浏览 vue-router 源码发现的,激励小伙伴们读一下,必定会播种满满!

2. react-router-dom 罕用的 2 种模式是 browserHistory、hashHistory,间接用 <BrowserRouter> 或 <HashHistory> 将根组件(通常是 <App>)包裹起来就能实现。

  • react-router 的实现依赖 history.js,history.js 是 JavaScript 库。<BrowserRouter>、<HashHistory> 别离基于 history.js 的 BrowserHistory 类、HashHistory 类实现。
  • BrowserHistory 类通过 pushState、replaceState 和 popstate 实现,但并没有相似 vue-router 的兼容解决。HashHistory 类则是间接通过 location.hash、location.replace 和 hashchange 实现,没有优先应用 history 新个性的解决。

嵌套路由与子路由

1. vue-router 嵌套路由

在 new VueRouter() 配置路由表时,通过定义 Children 实现嵌套路由,无论第几层的路由组件,都会被渲染到父组件 <router-view/> 标识的中央。

router.js

const router = new Router({
    mode:'history',
    routes: [{
        path: '/nest',
        name: 'nest',
        component: Nest,
        children:[{
            path:'first',
            name:'first',
            component:NestFirst
        }]
    }]
})

nest.vue

<div class="nest">
    一级路由 <router-view></router-view>
</div>

first.vue

<div class="nest">
    二级路由 <router-view></router-view>
</div>

/nest 下设置了二级路由 /first,二级对应的组件渲染在一级路由匹配的组件 <router-view/> 标识的中央。在配置子路由时,path 只须要是以后门路即可。

2. react-router 子路由

react-router 根组件会被渲染到 <Router/> 指定的地位,子路由则会作为子组件,由父组件指定该对象的渲染地位。如果想要实现上述 vue-router 嵌套的成果,须要这样设置:

route.js

const Route = () => (
    <HashRouter>
        <Switch>
            <Route path="/nest" component={Nest}/>
        </Switch>
    </HashRouter>
);

nest.js

export default class Nest extends Component {render() {
        return (
            <div className="nest">
                一级路由
                <Switch>
                    <Route path="/nest/first" component={NestFirst}/>
                </Switch>
            </div>
        )
    }
}

first.js

export default class NestFirst extends Component {render() {
        return (
            <div className="nest">
                二级路由
                <Switch>
                    <Route exact path="/nest/first/second" component={NestSecond}/>
                </Switch>
            </div>
        )
    }
}

其中,/nest 为一级路由,/fitst 二级路由匹配的组件,作为一级路由的子组件。react-router 定义子路由 path 时,须要写残缺的门路,即父路由的门路要残缺。

路由守卫

1. vue-router 导航守卫分为全局守卫、路由独享守卫、组件内的守卫三种。次要用来通过跳转或勾销的形式守卫导航。

a. 全局守卫

  • beforeEach — 全局前置钩子(每个路由调用前都会触发,依据 from 和 to 来判断是哪个路由触发)
  • beforeResolve — 全局解析钩子(和 router.beforeEach 相似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用)
  • afterEach — 全局后置钩子

b. 路由独享守卫

  • 路由配置上能够间接定义 beforeEnter 守卫。

c. 组件内守卫

  • beforeRouteEnter — 在渲染该组件的对应路由被 confirm 前调用,不能获取组件实例 this,因为当守卫执行前,组件实例还没被创立。
  • beforeRouteUpdate — 以后路由扭转,然而该组件被复用时调用
  • beforeRouteLeave — 导航来到该组件的对应路由时调用

2. react-router 4.0 版本之前,提供了 onEnter 和 onLeave 钩子,实现相似 vue-router 导航守卫的性能,但 4.0 版本后勾销了该办法。

路由信息

1. vue-router 中 $router、$route 对象

vue-router 在注册时,为每个 vue 实例注入了 $router、$route 对象。$router 为 router 实例信息,利用 push 和 replace 办法实现路由跳转,$route 提供以后激活的路由信息。

import router from './router'
export default new Vue({
    el: '#app',
    router,
    render: h => h(App),
})

2. react-router 中 history、location 对象

在每个由 <Route/> 包裹的组件中提供了 history、location 对象。利用 this.props.history 的 push、replace 办法实现路由导航,this.props.location 获取以后激活的路由信息。

const BasicRoute = () => (
    <div>
        <HeaderNav></HeaderNav>
        <HashRouter>
            <Switch>
                <Route exact path="/" component={Home}/>
            </Switch>
        </HashRouter>
    </div>
);

如果想要取得 history、location 肯定是 <Route /> 包裹的组件。所以在 <HeaderNav/> 中是无奈获取这两个对象的,而 <Home/> 组件是能够的。

vue-router 是全局配置形式,react-router 是全局组件形式,但两者出现给开发者的性能实际上是大同小异的。当然,vue-router 与 react-router 在应用上的差别不仅仅是小编说的这些。说到底,不论用什么样的形式实现,前端路由的实现原理都是不会变的。

总结

前端路由的初步体验马上就要完结了,在决定深入研究前端路由之前,小编自信满满,感觉应该不会破费很大的精力与工夫,可事实是,波及到的常识盲区越来越多,信念在逐步瓦解。好在终局不错,播种了很多,也心愿《SPA 路由三部曲之外围原理》这篇文章能让大家有所播种,哪怕只是一个知识点。

小编曾经在争分夺秒的筹备《SPA 路由三部曲之 MyVueRouter 实际》、《SPA 路由三部曲之 VueRouter 源码解析》过程中了,小编置信是不会让你悲观的,请充斥期待吧!

PS:文章中有些是个人观点,如果不对,欢送交换、斧正!

退出移动版