50、路由vue-router

参考:https://blog.csdn.net/qq_31967569/article/details/91546294

目录

1、对路由的理解?

2、路由的实现过程

3、前端路由的两种实现方式:基于hash 和基于history

3.1 hash模式

3.2 history模式

3.3 两种模式对比

4、Vue-Router 的懒加载如何实现

4.1 方案一(常用):使用箭头函数+import动态加载

 4.2 方案二:使用箭头函数+require动态加载

4.3 方案三:使用webpack的require.ensure技术,也可以实现按需加载。

5、 如何定义动态路由?如何获取传过来的动态参数?

5.1 param方式

5.2 query方式

5.3 params和query的区别?

6、Vue-router 导航守卫

6.1  全局的守卫

6.2 路由独享的守卫

6.3 组件内的守卫 

7、对前端路由的理解

8、Vue-router跳转和location.href有什么区别


1、对路由的理解?

路由,其实就是指向的意思。当我点击页面上的home按钮时,页面中就要显示home的内容,如果点击页面上的about 按钮,页面中就要显示about 的内容。Home按钮  => home 内容, about按钮 => about 内容,也可以说是一种映射. 所以在页面上有两个部分,一个是点击部分,一个是点击之后,显示内容的部分。 

点击之后,怎么做到正确的对应,比如,我点击home 按钮,页面中怎么就正好能显示home的内容。这就要在js 文件中配置路由

路由中有三个基本的概念 route, routes, router:

  1. route(路由信息对象):它是一条路由,由这个英文单词可看出 它是单数,Home按钮  => home内容, 这是一条route,  about按钮 => about 内容,这是另一条路由。route对象 {path:’/home’, component: home}。包括 path,params,hash,query,fullPath,matched,name 等路由信息参数;
  2. routes:是一组路由把上面的每一条路由组合起来,形成一个数组。[{home 按钮 =>home内容 }, { about按钮 => about 内容}];
  3. router(路由实例对象): 是一个机制,相当于一个管理者,它来管理路由。因为routes 只是定义了一组路由,它放在哪里是静止的,当真正来了请求,怎么办? 就是当用户点击home 按钮的时候,怎么办?这时router 就起作用了,它到routes 中去查找,去找到对应的 home 内容,所以页面中就显示了 home 内容。包括了路由的跳转方法,钩子函数等。
  4. 客户端中的路由:实际上就是dom 元素的显示和隐藏直接找到与地址匹配的一个组件或对象并将其渲染出来。改变浏览器地址而不向服务器发出请求当页面中显示home 内容的时候,about 中的内容全部隐藏,反之也是一样。客户端路由有两种实现方式基于hash 和基于history

2、路由的实现过程

在vue中实现路由还是相对简单的。因为页面中所有内容都是组件化的,我们只要把路径和组件对应起来就可以了,然后在页面中把组件渲染出来

  1. <router-link>:对应点击部分。to属性表示定义点击之后,要到哪里去。例 点击Home后,跳转到/home :<router-link  to="/home">Home</router-link> 
  2. <router-view>:对应显示部分。其实它就是一个占位符,它在什么地方,匹配路径的组件就显示在什么地方。<router-view></router-view>
<template>
  <div id="app">
    <header>
    <!-- router-link 定义点击后导航到哪个路径下 -->
      <router-link to="/home">Home</router-link>
      <router-link to="/about">About</router-link>
    </header>
    <!-- 对应的组件内容渲染到router-view中 -->
    <router-view></router-view>   
  </div>
</template>

(1)在js中配置路由。

首先要定义route对象,  一条路由的实现。由两个部分组成: 路径path和对应的组件component. 如:route对象 {path:’/home’, component: home}。两条路由,组成一个routes: 

const routes = [
  { path: '/home', component: Home },  //访问路径/home时,显示Home组件
  { path: '/about', component: About } //访问路径/about时,显示About组件
]

(2)创建router 对路由进行管理。它由构造函数 new vueRouter() 创建,接受routes 参数

const router = new VueRouter({
      routes // routes: routes 的简写
})

(3)配置完成后把router 实例注入到 vue 根实例中,就可以使用路由了

const app = new Vue({
  router
}).$mount('#app')

执行过程:

  1. 当用户点击 router-link 标签时,会去寻找它的 to 属性;
  2. 它的 to 属性和 js 中配置的路径 { path: '/home', component: Home}  对应,从而找到了匹配的组件;
  3. 最后把组件渲染到 <router-view> 标签所在的地方。

3、前端路由的两种实现方式基于hash 和基于history

3.1 后端路由

路由这个概念最先是后端出现的,后端路由又称服务器路由

服务器接收前端发来的http请求后,会根据url找到相应的映射函数,然后执行函数,并将返回值发回给客户端。

对于静态资源来说,映射函数就是一个文件读取操作;对于动态资源,映射函数可能是一个数据库读取操作,或者数据处理操作根据读取的数据在服务端使用相应的模板来渲染页面再返回渲染完毕的页面【⚠️这是早期的做法】

在浏览器输入网址后发起请求,返回来的 HTML 页面是最终呈现的效果(如下图)并且每次点击页面跳转,都会重新访问服务器,然后服务器返回HTML 资源。

  • 好处:安全性好、SEO好;(SEO 搜索引擎优化)

  • 缺点:加大服务器压力,不利于用户体验

3.2 hash模式

(1)简介:开发中默认的模式,它的URL带着一个#,例如:www.abc.com/#/vue,它的hash值就是#/vuewww.baidu.com/#/hashhash,它的hash值就是 “#/hashhash” 。(此 hash 不是密码学里的散列运算)早期的前端路由的实现就是基于location.hash来实现的

(2)特点hash值会出现在URL里面,但是不会出现在HTTP请求中对后端完全没有影响。所以改变hash值,不会重新加载页面。这种模式的浏览器支持度很好,低版本的IE浏览器也支持这种模式。hash路由被称为是前端路由,已经成为SPA(单页面应用)的标配。

(3)原理: hash模式的主要原理是onhashchange 事件。在当前 URL 的哈希值(以 '#' 号为开始) 发生改变时触发onhashchange 。使用hash模式时,我们可以考虑对每个路由注册一个映射函数,页面加载时,在window注册onhashchange事件的监听函数每次切换url,就调用对应的映射函数。如下图,当hash值变化时,触发onhaschange事件。

触发hash变化的方式主要有两种

  1. 直接修改url中的hash值;
  2. 点击后退或前进按钮时,hash值发生变化;
  3. 通过a标签,并为a设置href属性,当用户点击这个标签后,URL就会发生改变;
  4. 直接使用JavaScript来对loaction.hash进行赋值,从而改变当前url的hash值,触发onhashchange事件。

例如:点击按钮时,触发click事件,修改哈希值。 

使用onhashchange()事件的好处:

  1. 页面的hash值发生变化时,无需向后端发起请求,window就可以监听事件的改变,并按规则加载相应的代码;
  2. hash值变化对应的URL都会被浏览器记录下来,这样浏览器就能实现页面的前进和后退。虽然是没有请求后端服务器,但是页面的hash值和对应的URL关联起来了。

(4)如何获取页面的hash变化

    1)通过watch 监听$route的变化

// 监听,当路由发生变化的时候执行
watch: {
  $route: {
    handler: function(val, oldVal){
      console.log(val);
    },
    // 深度观察监听
    deep: true
  }
},

    2)window.location.hash读取#值 。window.location.hash 的值可读可写,读取来判断状态是否改变,写入时可以在不重载网页的前提下,添加一条历史访问记录 。

补充:使用hash模式的vue项目,当切换组件时,组件对应的路径会在hash值部分显示。

例如切换到组件User

3.3 history模式

到了HTML5,又提供了History API来实现URL的变化。其中做最主要的API有以下两个:history.pushState()和history.repalceState()。 

(1)简介:history模式的URL中没有#,它使用的是传统的路由分发模式,即用户在输入一个URL时,服务器会接收这个请求,并解析这个URL,然后做出相应的逻辑处理

(2)特点:当使用history模式时,URL就像这样:abc.com/user/id相比hash模式更加好看。但是,history模式需要后台配置支持。如果后台没有正确配置,访问时会返回404。所以,需要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回单页应用的 html 文件。

(3)History主要的API分为两大部分,切换历史状态修改历史状态

  • 修改历史状态:包括了 HTML5 History Interface 中新增的  pushState()   replaceState() 方法,这两个方法应用于浏览器的历史记录栈,提供了对历史记录进行修改的功能。只是当他们修改了url时,但浏览器不会立即向后端发送请求(即不进行刷新)。如果要做到改变url但又不刷新页面的效果,就需要前端用上这两个API。
  1. pushState() : 新增一个历史记录
  2. replaceState(): 直接替换当前的历史记录

     这两个api都接受三个参数:

window.history.pushState(state,title, "http://www.163.com");
  1. state:合法的 Javascript 对象,可以用在 popstate 事件中;
  2. title:现在大多浏览器忽略这个参数,可以直接用 null 代替;
  3. url:任意有效的 URL,用于更新浏览器的地址栏。
  • 切换历史状态: 包括forward()back()go()三个方法,对应浏览器的前进,后退,跳转操作。
  1. History.back():移动到上一个网址,等同于点击浏览器的后退键。对于第一个访问的网址,该方法无效果。
  2. History.forward():移动到下一个网址,等同于点击浏览器的前进键。对于最后一个访问的网址,该方法无效果。
  3. History.go():接受一个整数作为参数,以当前网址为基准,移动到参数指定的网址,比如go(1)相当于forward()go(-1)相当于back()如果参数超过实际存在的网址范围,该方法无效果;如果不指定参数,默认参数为0,相当于刷新当前页面

(4)History.pushState()方法用于在历史中添加一条记录

pushState()方法不会触发页面刷新,只是导致地址栏发生变化

  • state一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化以后保留在本地重新载入这个页面的时候,可以拿到这个对象。如果不需要这个对象,此处可以填null
  • title:新页面的标题。但是,现在所有浏览器都忽视这个参数,所以这里可以填空字符串。
  • url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

 假定当前网址是example.com/1.html,使用pushState()方法在浏览记录(History 对象)中添加一个新记录。

var stateObj = { foo: 'bar' };
history.pushState(stateObj, 'page 2', '2.html');

 添加新记录后,浏览器地址栏立刻显示example.com/2.html但并不会跳转到2.html,甚至也不会检查2.html是否存在,它只是成为浏览历史中的最新记录

这时,在地址栏输入一个新的地址(比如访问google.com),此时历史url为 2.html、1.html;

然后点击了倒退按钮,页面的 URL 将显示2.html;你再点击一次倒退按钮,URL 将显示1.html

(5)History.replaceState()方法用来修改 History 对象的当前记录,其他都与pushState()方法一模一样。

假定当前网页是example.com/example.html

history.pushState({page: 1}, 'title 1', '?page=1')
// URL 显示为 http://example.com/example.html?page=1

history.pushState({page: 2}, 'title 2', '?page=2');
// URL 显示为 http://example.com/example.html?page=2
此时历史记录包含: page=2、page=1、example.html

history.replaceState({page: 3}, 'title 3', '?page=3');
// URL 显示为 http://example.com/example.html?page=3
用page3替换了page2 此时历史记录包含:page3、page=1、example.html

history.back()
// URL 显示为 http://example.com/example.html?page=1
从当前页page3 返回到了 page1

history.back()
// URL 显示为 http://example.com/example.html
从当前页page1 返回到了 example.html

history.go(2)
// URL 显示为 http://example.com/example.html?page=3

(6)popstate 事件

每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件。

注意:

  • 仅仅调用pushState()方法或replaceState()方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用History.back()History.forward()History.go()方法时才会触发。
  • 另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。

参考:History 对象 -- JavaScript 标准参考教程(alpha)

(7)对于单页应用的 history 模式而言,url 的改变有三种方式:

  1. 点击浏览器的前进或后退按钮
  2. 点击 a 标签
  3. JS 中主动触发 history.pushState 和replaceState函数

方式1,3都能触发popstate ,所以我们只需要对a标签处理,通过对取消a标签的默认行为,然后调用pushState来触发路径变化。

(5)虽然history模式丢弃了丑陋的#。但是,它也有自己的缺点,就是在刷新页面的时候,如果没有相应的路由或资源,就会刷出404来。

如果想要切换到history模式,就要进行以下配置(后端也要进行配置):

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

3.4 两种模式对比

调用 history.pushState() 相比于直接修改 hash,存在以下优势:

  1. pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL
  2. pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
  3. pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串
  4. pushState() 可额外设置 title 属性供后续使用。
  5. hash模式下仅hash符号之前的url会被包含在请求中,后端如果没有做到对路由的全覆盖,也不会返回404错误history模式下,前端的url必须和实际向后端发起请求的url一致如果没有对用的路由处理,将返回404错误
  6. 使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存

hash模式和history模式都有各自的优势和缺陷,还是要根据实际情况选择性的使用。

3.5 原生JS版前端路由实现

(1)基于 hash 实现

HTML 部分:

<body>
  <ul>
    <!-- 定义路由 -->
    <li><a href="#/home">home</a></li>
    <li><a href="#/about">about</a></li>
 
    <!-- 渲染路由对应的 UI -->
    <div id="routeView"></div>
  </ul>
</body>

JavaScript 部分:

// 页面加载完不会触发 hashchange,这里主动触发一次 hashchange 事件
window.addEventListener('DOMContentLoaded', onLoad)
// 监听路由变化
window.addEventListener('hashchange', onHashChange)
 
// 路由视图
var routerView = null
 
function onLoad () {
  routerView = document.querySelector('#routeView')
  onHashChange()
}
 
// 路由变化时,根据路由渲染对应 UI
function onHashChange () {
  switch (location.hash) {
    case '#/home':
      routerView.innerHTML = 'Home'
      return
    case '#/about':
      routerView.innerHTML = 'About'
      return
    default:
      return
  }
}

 (2)基于 history 实现

HTML 部分:

<body>
  <ul>
    <li><a href='/home'>home</a></li>
    <li><a href='/about'>about</a></li>
 
    <div id="routeView"></div>
  </ul>
</body>

 JavaScript 部分:

// 页面加载完不会触发 hashchange,这里主动触发一次 hashchange 事件
window.addEventListener('DOMContentLoaded', onLoad)
// 监听路由变化
window.addEventListener('popstate', onPopState)
 
// 路由视图
var routerView = null
 
function onLoad () {
  routerView = document.querySelector('#routeView')
  onPopState()
 
  // 拦截 <a> 标签点击事件默认行为, 点击时使用 pushState 修改 URL并更新手动 UI,从而实现点击链接更新 URL 和 UI 的效果。
  var linkList = document.querySelectorAll('a[href]')
  linkList.forEach(el => el.addEventListener('click', function (e) {
    e.preventDefault()
    history.pushState(null, '', el.getAttribute('href'))
    onPopState()
  }))
}
 
// 路由变化时,根据路由渲染对应 UI
function onPopState () {
  switch (location.pathname) {
    case '/home':
      routerView.innerHTML = 'Home'
      return
    case '/about':
      routerView.innerHTML = 'About'
      return
    default:
      return
  }
}

每当处于激活状态的历史记录条目发生变化时,popstate事件就会在对应window对象上触发. 如果当前处于激活状态的历史记录条目是由history.pushState()方法创建,或者由history.replaceState()方法修改过的, 则popstate事件对象的state属性包含了这个历史记录条目的state对象的一个拷贝.

但是,调用history.pushState()或者history.replaceState()不会触发popstate事件. popstate事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮(或者在JavaScript中调用history.back()、history.forward()、history.go()方法).

 

参考:前端路由原理解析和实现_S筱潇S四维Smile-CSDN博客 

4、Vue-Router 的懒加载如何实现

 Vue  是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力。这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来

4.1 方案一(常用):使用箭头函数+import动态加载

import通过这种特殊注释语法,将几个路由放到一个组中 :

// 这三个组件分为一组:login_home_welcome
const Login = () => import(/* webpackChunkName: "login_home_welcome" */ '@/components/Login.vue')
const Home = () => import(/* webpackChunkName: "login_home_welcome" */ '@/components/Home.vue')
const Welcome = () => import(/* webpackChunkName: "login_home_welcome" */ '@/components/Welcome.vue')


const router = new Router({
  routes: [
    { path: '/login', component: Login },
    { path: '/home',  component: Home  },
    { path: '/welcome', component: Welcome }
  ]

(1)安装开发依赖@babel/plugin-syntax-dynamic-import

(2)在配置文件babel.config.js中声明该插件

(3)将路由改为按需加载的形式

其中 import(/*分组名:划分大同一分组的多个组件*/  路由组件)

 4.2 方案二:使用箭头函数+require动态加载

const router = new Router({
  routes: [
   {
     path: '/list',
     component: resolve => require(['@/components/list'], resolve)
   }
  ]
})

4.3 方案三:使用webpack的require.ensure技术,也可以实现按需加载。

这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件

//require.ensure(当前需要进来的模块的一些依赖, 回调函数 动态引入其他模块, 打包的组块名称)
const Foo = resolve => require.ensure([], () => resolve(require('./Foo.vue')), 'group-foo')
const Bar = resolve => require.ensure([], () => resolve(require('./Bar.vue')), 'group-foo')
const Baz = resolve => require.ensure([], () => resolve(require('./Baz.vue')), 'group-foo')
require.ensure(
  dependencies:String [],
  callback:function(require),
  errorCallback:function(error),
  chunkName:String
)

require.ensure()接受四个参数:

  • 第一个参数:表依赖关系,是一个数组,代表了当前需要进来的模块的一些依赖; 
  • 第二个参数: 是一个回调函数。这个有一个参数要求,通过回调函数的require可以动态引入组件。虽然require是回调函数的参数,但是其名称实际上是不能换的,否则WebPack就无法静态分析的时候处理它; 
  • 第三个参数:errorCallback比较好理解,就是处理错误的回调; 
  • 第四个参数:chunkName则是指定打包的组块名称。

5、 如何定义动态路由?如何获取传过来的动态参数?

5.1 param方式

  • 配置路由格式:使用冒号:绑定动态参数。 /user/:userid
const routes = [{ //配置路由的index.js文件
    path: '/user/:userid',
    name: 'users',
    component: User
}]
  • 传递的方式:在path后面跟上对应的值  '/user/'+id
<router-link :to="'/user/'+id" >用户</router-link>
  • 传递后形成的路径:/router/123

(1)路由定义

const routes = [{ //配置路由的index.js文件
    path: '/user/:userid',
    name: 'users',
    component: User
}]

(2)路由跳转

  1)方法一 使用router-link实现路由跳转:使用字符串,在路径后面直接跟上对应的值

属性名userId使用自己的id,不必与路由配置的路径中一致 

<router-link :to="'/user/'+userId" replace>用户</router-link> 

  2)方法二  使用router-link实现路由跳转:使用对象的方式。

name: 'users' 的属性名和属性值 必须跟配置路由中的一致
params中的属性名userid  必须与配置路由path中的'/user/:userid'一致

<router-link :to="{ name: 'users', params: { userid: userId}}">用户</router-link>

 配置路由的index.js文件中,需定义name属性,才可使用方法二

3)方法三:使用$router的方式进行路由的跳转

this.$router.push('/user/' + this.userId)

// 或
this.$router.push({
   name: 'users', //属性名和属性值 必须与路由一致
   params: { // id属性名 必须与路由一致
     userid: this.userId
   }
})

配置路由的index.js文件中,需定义name属性,才可使用方法三 

(3)参数获取

1)方式一:通过 $route.params.userid 获取传递的值(userid 表要查询的参数)。

2)方式二:组件中也可以用props来接受参数,前提是需要在路由配置中设置props为true

const routes = [{ //配置路由的index.js文件
    path: '/user/:userid',
    name: 'users',
    component: User,
   //ture,代表将path后面的参数作为值,传递到组件中,组件中通过props属性接受这个值
    props:true
}]

在组件中通过props接收参数 

<script>   // 组件中
export default {
	 props:{
		 name:{
			 type:String,
			 default:'lily' //默认情况
		 },
		 id:{
			 type:Number,
			 default:'0' //默认情况
		 }
	 }
}
</script>

3)$router 和 $route的区别

  • $router : 是路由操作对象,只写对象
  • $route : 路由信息对象,只读对象

5.2 query方式

  • 配置路由格式:/router,也就是普通配置
const routes = [{ //配置路由的index.js文件
    path: '/user',
    name: 'users',
    component: User
}]
  • 传递的方式:对象中使用query的key作为传递方式。

       注意:query传参的方式只可以通过对象,不可以使用字符串。

                  里面的属性名(如本例中的id)可以随便起名,不像params方式传参时受限

<router-link :to="{path:'/user',query:{id:18, name:'sh'}">用户</router-link>
  • 传递后形成的路径:/route?id=18 & name=sh。参数之间用&符号连接
localhost:8080/user?id=18 & name=sh

(1)路由定义(普通的路由配置)

const routes = [{ //配置路由的index.js文件
    path: '/user',
    name: 'users',
    component: User
}]

(2)路由跳转

    1)方法一 使用router-link方式实现路由跳转:使用name

属性名name 和 其属性值 需与路由配置中的 name: 'users' 一致

<router-link :to="{ name: 'users', query: { uname: james }}">按钮</router-link>

    2)方法二 使用router-link方式实现路由跳转:使用path

<router-link :to="{ path: '/user', query: { uname:james }}">按钮</router-link>

   3)方法三 使用$router的方式进行路由的跳转

// 方式1 使用name
this.$router.push({ name: 'users', query:{ uname:james }}) 

// 方式2 使用路径
this.$router.push({ path: '/user', query:{ uname:james }})

// 方式3 直接将传的参添加到路径后
this.$router.push('/user?uname=' + jsmes)

(3)参数获取

    1)方式一:通过 $route.query.userid 获取传递的值(userid 表要查询的参数)。

    2)方式二:组件中也可以用props来接受参数,前提是需要在路由配置中设置props为true

  与params中的方式二一样。

5.3 params和query的区别?

  1. params方式路由的引入只能用name;query方式路由的引入可以用name和path。
  2. params的参数是URL不可或缺的一部分,一定要加路由后面添加参数path: '/user/:userid',不添加刷新页面数据会丢失;而query是拼接在url后面的参数,路由后面不添加也没关系。
  3. params 在浏览器地址栏中不显示参数;query则显示。params相当于post请求,参数不会在地址栏中显示;query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数。
  4. params在浏览器地址栏中不显示参数名称

    http://47.107.171.252:8001/#/detail/123456

    query在浏览器地址栏中显示参数名称

    http://47.107.171.252:8001/#/detail?id=123456

6、Vue-router 导航守卫

  1. 全局的守卫:beforeEach(全局前置)、beforeResolve(全局解析)、afterEach(全局后置);写在router.js 文件中;
  2. 路由独享的守卫:beforeEnter;写在router单个路由中
  3. 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave;写在组件中。

优先级:①>②>③,组件内的守卫优先级是最低的

6.1  全局的守卫

(1)beforeEach(全局前置守卫)

对用于访问网页的情况进行相应的跳转。例如初次登入,不能访问除登录页面外的页面,已登录会自动跳转自首页等等

const router = new VueRouter({...})
 
//使用router.beforeEach注册一个全局前置守卫
router.beforeEach((to,from,next) =>{
    //...
})
  1. to: 将要访问的路径;
  2. from:当前导航正要离开的路由;
  3. next 是一个函数,表示放行。 next()  放行、next('/login')  强制跳转到到该路径。 确保要调用next方法,否则钩子就不会被resolved 。

(2)beforeResolve(全局解析)

这和router.beforeEach类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用

(3)afterEach(全局后置)

 不同的是,后置守卫不会接收next函数,也不会改变导航本身

router.afterEach((to,from)=>{
   //...
})

6.2 路由独享的守卫

单个路由独享的守卫,可以在单个路由中直接定义(方法、参数等都和全局前置守卫beforeEach一样)

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }]})

6.3 组件内的守卫 

都有三个参数:to, from, next

  1. beforeRouteEnter:路由进入之前调用。不能获取组件实例 `this` ,因为当守卫执行前,组件实例还没被创建。
  2. beforeRouteUpdate:路由更新调用。在当前路由改变,但是该组件被复用时调用。
  3. beforeRouteLeave:导航离开该组件的对应路由时调用。用来禁止用户在还未保存修改前突然离开。
watch: {
    $route(to, from) {
      //当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象
      // 对路由变化作出响应...
      console.log(" // 对路由变化作出响应...");
    }
}, //监听
beforeRouteEnter(to, from, next) {
    //路由进入之前调用
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
    //console.log(from);//上一个激活的路由
    //console.log(to);//当前路由
    next(vm => {
      // 通过 `vm` 访问组件实例
      console.log(vm)//当前激活的路由对应的组件对象
    });
    // next();
  },
  beforeRouteUpdate(to, from, next) {
    //路由更新调用
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
    // console.log(to);
    next();
  },
  beforeRouteLeave(to, from, next) {
    //这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
    //to是将要去的路由
    //from是将要离开的路由(也就是当前的路由)
    const answer = window.confirm(
      "Do you really want to leave? you have unsaved changes!"
    );
    if (answer) {
      next();
    } else {
      next(false);
    }
  },

补充:钩子函数

  • 是个函数,在系统消息触发时被系统调用 ,在系统级对所有消息、事件进行过滤;
  • 不是用户自己触发的

7、对前端路由的理解

在前端技术早期,一个 url 对应一个页面,如果要从 A 页面切换到 B 页面,那么必然伴随着页面的刷新。这个体验并不好,不过在最初也是无奈之举——用户只有在刷新页面的情况下,才可以重新去请求数据。

后来,改变发生了——Ajax 出现了,它允许人们在不刷新页面的情况下发起请求;与之共生的,还有“不刷新页面即可更新页面内容”这种需求。在这样的背景下,出现了 SPA(单页面应用)。

SPA极大地提升了用户体验,它允许页面在不刷新的情况下更新页面内容,使内容的切换更加流畅。但是在 SPA 诞生之初,人们并没有考虑到“定位”这个问题——在内容切换前后,页面的 URL 都是一样的,这就带来了两个问题:

  • SPA 其实并不知道当前的页面“进展到了哪一步”。可能在一个站点下经过了反复的“前进”才终于唤出了某一块内容,但是此时只要刷新一下页面,一切就会被清零,必须重复之前的操作、才可以重新对内容进行定位——SPA 并不会“记住”你的操作。
  • 由于有且仅有一个 URL 给页面做映射,这对 SEO 也不够友好,搜索引擎无法收集全面的信息

为了解决这个问题,前端路由出现了。

前端路由可以帮助我们在仅有一个页面的情况下,“记住”用户当前走到了哪一步——为 SPA 中的各个视图匹配一个唯一标识。这意味着用户前进、后退触发的新内容,都会映射到不同的 URL 上去。此时即便他刷新页面,因为当前的 URL 可以标识出他所处的位置,因此内容也不会丢失

那么如何实现这个目的呢?首先要解决两个问题:

  • 当用户刷新页面时,浏览器会默认根据当前 URL 对资源进行重新定位(发送请求)。这个动作对 SPA 是不必要的,因为我们的 SPA 作为单页面,无论如何也只会有一个资源与之对应。此时若走正常的请求-刷新流程,反而会使用户的前进后退操作无法被记录。
  • 单页面应用对服务端来说,就是一个URL、一套资源,那么如何做到用“不同的URL”来映射不同的视图内容呢?

从这两个问题来看,服务端已经完全救不了这个场景了。所以要靠咱们前端自力更生,不然怎么叫“前端路由”呢?作为前端,可以提供这样的解决思路:

  • 拦截用户的刷新操作,避免服务端盲目响应、返回不符合预期的资源内容。把刷新这个动作完全放到前端逻辑里消化掉。
  • 感知 URL 的变化。这里不是说要改造 URL、凭空制造出 N 个 URL 来。而是说 URL 还是那个 URL,只不过我们可以给它做一些微小的处理——这些处理并不会影响 URL 本身的性质,不会影响服务器对它的识别,只有我们前端感知的到。一旦我们感知到了,我们就根据这些变化、用 JS 去给它生成不同的内容。

8、Vue-router跳转和location.href有什么区别

  • 使用 location.href= /url 来跳转,简单方便,但是刷新了页面;
  • 使用 history.pushState( /url ) ,无刷新页面,静态跳转;
  • 引进 router ,然后使用 router.push( /url ) 来跳转,使用了 diff 算法,实现了按需加载,减少了 dom 的消耗。其实使用 router 跳转和使用 history.pushState() 没什么差别的,因为vue-router就是用了 history.pushState() ,尤其是在history模式下。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值