vue.js备忘记录(五) vue-router

如果我们采用SPA(单网页应用)的设计方式,服务器会把前端文件一次性发过来,前端通过监听url的改变,选择展示那些内容,也就是前端路由

一. 如何改变url但是页面不刷新?

方式一: 改变哈希值hash

比如,我们随便找一个网页

我们在浏览器控制台输入

发现网站的url有了些改变

查看network却没有任何请求

方式二 history模式的方法

(1). history.pushState()压栈式

比如,我们随便找一个网页

我们在浏览器控制台输入

history.pushState({},'','home')

发现网站的url有了些改变

image

查看network却没有任何请求

image

这种压栈式方法,可以使用浏览器返回上一页按钮弹栈,实际上是在调用

history.back()

也可以使用history.go() 操作栈

history.forward() 等价于  history.go(1) 
history.back() 等价于  history.go(-1)

(2): history.replaceState()非压栈式

history.replaceState({},'','home')

二. 认识vue-router

1.安装 vue-router

cnpm install vue-router --save

我们也可以在用CLi项目创建时就选择好 vue-router 这样项目创建时会送我们一个hello world的例子,我们可以先体验一下

2.创建文件夹router,创建index.js

用来存放我们的路由配置

3.路由创建过程

首先,我们在index.js创建一个vuerouter并暴露

//1.导入VueRouter
import VueRouter from 'vue-router'
//2.导入Vue   (因为后面要用Vue.use())
import Vue from 'vue'


//3.通过Vue.use安装一下插件
Vue.use(VueRouter)

//4.创建路由对象
routes=[]

const router = new VueRouter({
  routes
})

//5.暴露路由对象
export default router

在main.js中,我们挂载这个vuerouter

import Vue from 'vue'
import App from './App.vue'
import router from './router'     //6.导入暴露的vuerouter (/index.js是默认,路径中省略了)

Vue.config.productionTip = false

new Vue({
  router,      //7. 将导入的router传给自己的router属性
  render: h => h(App),
}).$mount('#app')

4.配置映射关系

上面的模板中,routes=[], 我们没有配置映射关系, 现在我们来配置一下:

  • 第一步,导入页面组件

  • 第二步,创建映射关系

  • 第三步,在app.vue模板中(上级组件),添加router-link标签,这是一个触发url改变的入口

  • 第四步,在app.vue模板中(上级组件),添加router-view占位符标签,确定请求过来的内容的占位

在router/index.js中

import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '../components/Home'  //1. 导入第一个组件Home
import About from '../components/About' //1. 导入第二个组件About

Vue.use(VueRouter)
const routes = [
  {
    path: '/',       //2.添加映射关系
    component: Home
  },
  {
    path: '/about',  //2.添加映射关系
    component: About
  }
]

const router = new VueRouter({
  routes
})

export default router

在App.js中

<template>
  <div id="app">
    <router-link to="/home">首页</router-link>   
    <!-- 3.使用路由 通过router-link标签,配置to属性提供链接目标,点击此标签会触发对应的url请求-->
    <router-link to="/about">关于</router-link>
    <!-- 3.使用路由 通过router-link标签,配置to属性提供链接目标,点击此标签会触发对应的url请求-->
    <router-view></router-view>
    <!-- 4.使用路由 通过router-view占位符标签,确定请求过来的内容的占位-->
  </div>
</template>

<script>
export default {
  name: 'App',
}
</script>

<style scoped>
</style>

路由重定向:

const routes = [
  {
    path:'',
    redirect:'home'
  },
]

5.将默认的哈希方式改为history模式

vue默认是使用hash方式的,

我们可以给它改成history模式,

这需要,我们在router/index.js中创建VueRouter时传入另一个参数

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

此外我们在cli初始化项目时,也可以默认history模式

6.router-link 属性

  • to属性 :指向的url地址

  • tag属性: 默认是a标签,如果tag='button',则渲染成标签.此外还可以是 <li>等

  • replace属性: 采用非压栈式跳转,不可返回

同时,我们注意到,当某个标签被点击时,他的class会自动添加两个类:router-link-exact-active和router-link-active

image

有了这两个class,我们可以方便的写样式了

image

7. 不用router-link,通过代码修改路径 $router.push

上面的我们都是使用的 <router-link>来触发的url改变 有没有办法用普通的表单元素触发呢?

有的.比如一个button 我们绑定了他的单击事件 btnClick,则我们可以在方法里写:

每个组件都有一个全局对象叫$router,这个

btnClick(){
    this.$router.push('/home')
}

上面是压栈式,当然也有非压栈式

btnClick(){
    this.$router.replace('/home')
}

三. 动态路由 /:参数

很多时候我们的url可能是不固定的,访问的页面的具体信息也会随着url改变,这就称之为动态路由. 例如: 我们访问张三的资料 /user/zhangsan 李四的是 /user/lisi

注册动态路由的方法是 /:参数

我们注册映射关系时,这样注册

  {
    path:"/user/:userId",
    component:User
  }

我们触发url时这样触发

//在<router-link>上动态绑定
<router-link :to="'/user/'+userId">用户</router-link> 

//或者通过普通标签的事件:
    btnClick(){
      this.$router.push('/user/'+this.userId)
    }

那么url跳转后如何在先页面中拿到跳转的参数呢? 用$route

$route   :当前活跃路由 每个组件都有一个全局对象$route
一定要和$router区分开!!!!!

每个组件都有一个route全局对象 (不是router,少了一个r)

image

我们看到这个 对象里有一个属性params ,里面有一个键值对 就是我们想要的,这时我们制作一个计算属性放在我们想显示的位置就好了

  computed: {
    userId(){
      return this.$route.params.userId 
    }
  },

四. 路由的懒加载

当我们打包时,我们的bundle.js会变得特别大,太大的bundle会使得加载变慢,从而出现浏览器空白.但其实即使是单页程序,也无需浪费这段时间,同时给用户不好的体验.我们完全可以使用懒加载,将刚开始用不到的代代码先不加载,等用户触发了相关路由时再加载

懒加载说白了就是不同的路由打包到不同的js里去

懒加载如何实现??

我们不要在一开始就import 而是在用到的地方,用箭头函数直接匿名import

    path: '/about',  //2.添加映射关系
    component: ()=>import('../components/About')
  },

这样,我们可以让首页之外的路由选择懒加载,都懒加载也可以

五. 路由的嵌套

为什么要路由嵌套?看下图:

我们想在home页面请求路由,把路径改为/home/news, 但是页面还是在home页,只不过绿框里的内容改为了news组件的内容

如何实现路由嵌套?

1.映射路由

因为还是在home页面,所以不能直接写路由映射,而是将路由映射嵌套在home的映射中

具体做法是,将子映射写在父映射的children属性里

 {
    path: '/home',       //这是home的映射
    component: Home,
    children:[           //它里面可以定义一个children,里面放子映射
      {
        path: '',       //添加默认子映射
        redirect:'news'
      },
      {
        path: 'news',                   //添加子映射关系 路径只写名字 news
        component: ()=>import('../components/HomeNews')
      },
      {
        path: 'msgs',                   //添加子映射关系
        component: ()=>import('../components/HomeMsg')
      },
    ]
  },

接下来我们在home.vue中确定显示位置 路径照实写

<template>
  <div>
    <h2>我是首页</h2>
    <p>我是首页内容</p>
    <router-link to="/home/news">看看新闻</router-link>
    <router-link to="/home/msgs">看看消息</router-link>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "Home"
};
</script>

<style scoped>
</style>

六. 传递参数的路由

传递参数的两种方式

  • 配置动态路由 /router/:参数

  • query类型的传递 请求路径还是/router 但是同时会传一个query,如: /router?id=123

如果只需要传递一个符号,则选择第一种,如果信息较多,传递第二种

第一种我们之前已经接触了,现在看

1. 如何用query传递参数

只需要在<router-link> 的to属性里绑定一个对象,对象有如下属性:

<router-link :to="{
  path:'/profile',     <--path属性还是路由链接-->
  query:{              <--query属性里面传入一个对象,对象里都是键值对--> 
      id:'lili',
      age:20,
      gender:'girl'
    }}">Profile</router-link>

新页面里如何取?还是用之前用过的$route对象

this.$route.query

2.不用<router-link>怎么传query??

router.push和router.replace传入上述对象即可

  methods: {
    btnClick(){
      this.$router.push({
        path:'/profile',
        query:{
          name:"lili",
          age:18,
          gender:'girl'
        }
      })
    }
  }

七. keep-alive

一个页面如果跳转到了另一个页面,那么这个页面会被销毁,此时如果用户又返回这个页面,又需要重新加载.

keep-alive主要解决离开页面又返回时,页面需要重构的问题,这种重构很多时候都是没必要的

1. 如何使用keep-alive?

  • <router-view>其实也是个组件,如果它直接被包在<keep-alive>里面,所有涉及到的视图组件都会被缓存

  • <keep-alive>是定义在Vue中的内置组件,目的是为了避免重新渲染

例:我们想让home的两个次级路由的组件来回切换时不重新构建,可以给控制他们显示的<router-view>用<keep-alive>包起来

<template>
  <div>
    <h2>我是首页</h2>
    <p>我是首页内容</p>
    <router-link to="/home/news">看看新闻</router-link>
    <router-link to="/home/msgs">看看消息</router-link>
    <keep-alive>
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

<script>
export default {
  name: "Home"
};
</script>

<style>
</style>

2. 因为被keep-alive,子组件多了两个属性

被keep-alive的组件都会多两个属性 activated 和 deactivated,里面可以传入方法,当活跃/不活跃时调用

举例:

<template>
  <div>
    <h1>您有4条短消息</h1>
    <ul>
      <li>短消息1</li>
      <li>短消息2</li>
      <li>短消息3</li>
      <li>短消息4</li>
    </ul>
  </div>
</template>

<script>
export default {
  name:"HomeMsg",
  activated() {
    console.log("我真活跃");
  },
  deactivated() {
    console.log("我不活跃了");
  },
}
</script>

<style>

</style>

3.<keep-alive> 的包含和例外

如果又4个标签,我们想其中3个keep-alive怎么办???

这时我们需要用keep-alive的属性

    <keep-alive exclude="HomeMsg,User">    这会排除这两个组件的keep-alive
      <router-view></router-view>
    </keep-alive>

八. 导航守卫

有时我们需要监听页面跳转

比如,我们要监听页面跳转,跳转时,将我们的网站的title该为对应页面的名称

1.前置守卫

前置守卫是跳转前的守卫

我们可以在index.js里,给我们的router调用一个前置守卫(钩子)函数:router.beforeEach()

router.beforeEach((to,from,next)=>{   //这就是页面跳转过程
  document.title= to.meta.matched[0].title  //将页面标题改为了to路由的meta信息里的标题
  next()  // 必须调用next(),以维护链式
})

前提是to路由里有meta,如: meta(描述信息)

  {
    path:"/profile",
    component:()=>import('../components/Profile'),
    meta:{
      title:'档案'
    },
  },

为什么有个 .matched[0], 因为有时我们进入嵌套路由,比如, home/news,

但此时我们还想显示home的meta, 就可以通过这种方法找到它的第一级路由的meta
比如: 如果用户登录了,其他页面可以正常跳转和访问,如果没登录,跳转到登录界面

// 挂载路由导航守卫
router.beforeEach((to, from, next) => { // 这就是页面跳转过程
  if (to.path === '/login') next()
  const token = window.sessionStorage.getItem('token')
  if (!token) return next('/login')
  next()
})

2.后置守卫

其实还有后置守卫(钩子):

router.afterEach(to,from)

后置守卫没有next

3.next有大用

  • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。

  • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

  • next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to proprouter.push 中的选项。

  • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

4.路由独享守卫

你可以在路由配置上直接定义 beforeEnter 守卫:

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

5.组件内守卫

你可以在路由组件内直接定义以下路由导航守卫:

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}
beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。

beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容