Vue教程-5-路由vue-router

目录

1.前端渲染/后端渲染/前端路由/后端路由

1.1.后端渲染

1.2.后端路由

1.3.后端路由缺点

1.4.前后端分离阶段

1.5.前端渲染

1.6.前端路由与SPA单页面富应用阶段

2.页面刷新与前端路由

2.1.URL的hash模式

2.2.HTML5的history模式

3.基于CLI2的vue-router安装与配置

4.路由默认值

5.修改路由模式

6.详解

7.通过代码跳转路由

8.动态路由

9.vue-router打包与懒加载

10.vue-router嵌套路由

11.vue-router传递参数

12.vue-router全局导航守卫

12.1.扩展

13.keep-alive与vue-router

14.扩展:Vue Router官网读后感

14.1.Vue Router与Vue关系

14.2.官方链接

14.3.官方案例

14.4.案例总结

14.5.动态路由匹配

14.5.1.为什么会出现这个知识点?

14.5.2.为什么会出现这个知识点:响应路由参数的变化

14.5.3.为什么会出现这个知识点:捕获所有路由或404 Not found路由

14.5.4.为什么会出现这个知识点:高级匹配模式

14.5.5.为什么会出现这个知识点:匹配优先级

14.6.编程式的导航

14.6.1.router.push

14.6.2.router.replace

14.6.3.router.go

14.6.4.操作History

14.7.命名路由

14.7.1.方式1:$route.params.XXX

14.8.命名视图

14.9.重定向和别名

14.10.路由组件传参

14.10.1.方式2:props解耦


1.前端渲染/后端渲染/前端路由/后端路由

1.1.后端渲染

WEB早期是由JSP+HTML+CSS组成,WEB服务器会把请求结果全部转化为HTML+CSS,这个时期的浏览器其工作就是把接收到的代码进行展示;

1.2.后端路由

后端服务器自己处理URL与页面之间的映射关系,早期的MVC模式,整个映射关系都配置在一个XML文件中;

1.3.后端路由缺点

①整个页面模块由后端人员来编写和维护的,早期的时候没有前端工程师,只有美工负责切图,然后剩下的工作全部交给后端人员来完成;

②需要嵌入JAVA等后端语言代码,整个页面既有HTML代码,也有CSS代码,同样还有后端语言代码;

1.4.前后端分离阶段

随着ajax的出现,这种开发模式有个突破:

①后端只提供API来返回数据,前端通过ajax收到数据后,自己通过js来将数据渲染到THML中;

②前后端责任分离,使得后端只注重数据,前端只注重交互与可视化;

③即便IOS/Android出现后,后端不需要进行任何修改;

1.5.前端渲染

HTML+CSS从静态资源服务器来获取,然后前端浏览器自己通过ajax来获取数据,然后数据的渲染是自己来完成;

1.6.前端路由与SPA单页面富应用阶段

这里随着ajax出现,使得前后端分离,然后更进一步发展出现了SPA(simple page web application)阶段,即单页面富应用;

之前,一个WEB应用会有N多个Html文件以及与Html文件配套的CSS、JS文件一起来组成;

到了“单页面富应用”阶段,整个WEB应用只有1个Html文件,甚至只有1个js、1个css文件组成,剩下的所有的展示、渲染等工作,全部交给js代码来完成;

这里可以参考webpack案例工程,其打包的时候,会把全部js、全部css、一部分小图片全部融合到一个js文件中,这就很恐怖了

为了实现这种效果,这里就需要前端路由了,其功能是:当用户点击某个按钮的时候,前端路由会根据该按钮对应的url查找其要展示的内容,然后把要展示的内容渲染到入口index.html中的对应的区域;

(这里的“要展示的内容”看做是Vue的自定义组件,也就说:vue-router会根据URL找到对应的自定义组件,然后根据自定义组件的el属性找到index.html中的位置,最后再把template属性中的内容替换掉el属性绑定的html标签;)

2.页面刷新与前端路由

在上一节中我们得知,vue-router通过URL与组件的映射来实现了SPA的单页面富应用的效果,这就意味着当你按下F5后,刷新页面的代价是比较高的,这里就必须考虑到一件事情:当用户点击某个按钮,然后该按钮修改URL的同时,又不会触发浏览器去刷新整个页面;

要达到这种效果,有2种解决方法:

2.1.URL的hash模式

通过location.hash的方式来修改URL链接,例如:

修改之前:

修改之后:

URL也修改了,但页面没有刷新;

2.2.HTML5的history模式

history.pushState

history.pushState方法采用类似于栈的数据结构,即后进先出,可以通过history的back/go方法一层一层进行返回:

history.replaceState

这个是直接替换,而不是push压入:

history.go

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

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

3.基于CLI2的vue-router安装与配置

vue-router是vue.js的官方插件,与vue.js深度集成,官方地址:https://router.vuejs.org/

①安装vue-router:Npm install vue-router --save

②在模块中使用它:

第一步:导入路由对象,并且调用Vue.use(VueRouter)

第二步:创建路由实例,并且传入路由映射配置

第三步:在Vue实例中挂载创建的路由实例

定义路由配置src/router/index.js

// 导入Vue

import Vue from 'vue'

// 导入路由

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

import home from '@/components/home'

import page1 from '@/components/page1'

import page2 from '@/components/page2'

// 第一步:通过Vue.use(插件),来全局安装插件

Vue.use(Router)

// 第二步:创建路由对象

export default new Router({

  // 第三步:配置路由与组件之间的映射关系

  routes: [

    {

      // 路径名

      path: '/',

      name: 'HelloWorld',

      // 该路径对应的组件

      component: home

    },

    {

      // 路径名

      path: '/home',

      // 该路径对应的组件

      component: home

    },{

      // 路径名

      path: '/page1',

      // 该路径对应的组件

      component: page1

    },

    {

      // 路径名

      path: '/page2',

      // 该路径对应的组件

      component: page2

    }

  ]

})

// 第四步:将路由router挂载到Vue实例中

// 第五步:<router-link/>显示链接和<router-view/>自定义组件显示位置

在入口Vue实例中挂载路由mainjs

import Vue from 'vue'

import App from './App'

// 这里并没有详细说出/router/index.js文件,那是因为Vue默认找的就是该目录下名字叫index的js文件

// 第四步:将路由router挂载到Vue实例中

import router from './router'

Vue.config.productionTip = false

/* eslint-disable no-new */

new Vue({

  el: '#app',

  // 第四步:将路由router挂载到Vue实例中

  router,

  render: h => h(App)

})

在入口组件App.vue中排版

<template>

  <div id="app">

<!--    <img src="./assets/logo.png">-->

    <router-view/>

    <router-link to="/home">home.js</router-link>

    <router-link to="/page1">page1.js</router-link>

    <router-link to="/page2">page2.js</router-link>

    <table>

      <tr>

        <td>测试router-view的排版布局</td>

        <td><router-view></router-view></td>

      </tr>

    </table>

  </div>

</template>

<script>

export default {

  name: 'App'

}

</script>

<style>

</style>

4.路由默认值

定义路由配置src/router/index.js

// 导入Vue

import Vue from 'vue'

// 导入路由

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

import home from '@/components/home'

import page1 from '@/components/page1'

import page2 from '@/components/page2'

// 第一步:通过Vue.use(插件),来全局安装插件

Vue.use(Router)

// 第二步:创建路由对象

export default new Router({

  // 第三步:配置路由与组件之间的映射关系

  routes: [

    {

      // 路径名,首页这里默认就是/,这里注意下

      path: '/',

      // 重定向前端工程的首页,注意这里写的是path路径,而不是component的名字

      redirect: '/home'

    },

    {

      // 路径名

      path: '/home',

      // 该路径对应的组件

      component: home

    },{

      // 路径名

      path: '/page1',

      // 该路径对应的组件

      component: page1

    },

    {

      // 路径名

      path: '/page2',

      // 该路径对应的组件

      component: page2

    }

  ]

})

// 第四步:将路由router挂载到Vue实例中

// 第五步:<router-link/>显示链接和<router-view/>自定义组件显示位置

5.修改路由模式

默认是URL的hash模式,特征就是URL总是带有“#”号,这里通过Router的mode属性来修改:

定义路由配置src/router/index.js

// 导入Vue

import Vue from 'vue'

// 导入路由

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

import home from '@/components/home'

import page1 from '@/components/page1'

import page2 from '@/components/page2'

// 第一步:通过Vue.use(插件),来全局安装插件

Vue.use(Router)

// 第二步:创建路由对象

export default new Router({

  // 从URL的hash模式,变成history模式

  mode: 'history',

  // 第三步:配置路由与组件之间的映射关系

  routes: [

    {

      // 路径名,首页这里默认就是/,这里注意下

      path: '/',

      // 重定向前端工程的首页,注意这里写的是path路径,而不是component的名字

      redirect: '/home'

    },

    {

      // 路径名

      path: '/home',

      // 该路径对应的组件

      component: home

    },{

      // 路径名

      path: '/page1',

      // 该路径对应的组件

      component: page1

    },

    {

      // 路径名

      path: '/page2',

      // 该路径对应的组件

      component: page2

    }

  ]

})

// 第四步:将路由router挂载到Vue实例中

// 第五步:<router-link/>显示链接和<router-view/>自定义组件显示位置

6.<router-link/>详解

to:用于指定跳转的路径;

tag:可以指定<router-link/>之后渲染成什么组件,比如可以渲染成一个button、div、a等标签;

replace:replace不会留下history记录,用replace取代了pushState;

active-class:当<router-link/>对应的路由匹配成功时,会自动给当前元素设置一个router-link-active的class样式,设置active-class可以修改默认的样式,即当你点击<router-link/>后呈现的样式;

定义路由配置src/router/index.js

// 导入Vue

import Vue from 'vue'

// 导入路由

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

import home from '@/components/home'

import page1 from '@/components/page1'

import page2 from '@/components/page2'

// 第一步:通过Vue.use(插件),来全局安装插件

Vue.use(Router)

// 第二步:创建路由对象

export default new Router({

  // 一次性全部修改完router中匹配后,router-link颜色

  linkActiveClass: 'active',

  // 从URL的hash模式,变成history模式

  mode: 'history',

  // 第三步:配置路由与组件之间的映射关系

  routes: [

    {

      // 路径名,首页这里默认就是/,这里注意下

      path: '/',

      // 重定向前端工程的首页,注意这里写的是path路径,而不是component的名字

      redirect: '/home'

    },

    {

      // 路径名

      path: '/home',

      // 该路径对应的组件

      component: home

    },{

      // 路径名

      path: '/page1',

      // 该路径对应的组件

      component: page1

    },

    {

      // 路径名

      path: '/page2',

      // 该路径对应的组件

      component: page2

    }

  ]

})

// 第四步:将路由router挂载到Vue实例中

// 第五步:<router-link/>显示链接和<router-view/>自定义组件显示位置

在入口组件App.vue中排版

<template>

  <div id="app">

<!--    <img src="./assets/logo.png">-->

    <router-view/>

    <!-- /home这里的active-class的样式,覆盖了Vue自己的router-link-active样式 -->

    <router-link to="/home" tag="button" replace active-class="active">home.js</router-link>

    <router-link to="/page1" tag="li" replace>page1.js</router-link>

    <router-link to="/page2" replace>page2.js</router-link>

    <table>

      <tr>

        <td>测试router-view的排版布局</td>

        <td><router-view></router-view></td>

      </tr>

    </table>

  </div>

</template>

<script>

export default {

  name: 'App'

}

</script>

<style>

.router-link-active {

  color: red;

}

  .active {

    color:blue;

  }

</style>

7.通过代码跳转路由

router组件已经注册到Vue中,因此,你可以在任何一个组件中通过this.$router方式来获取router对象,进而调用push、replace方法来进行跳转:

this.$router.push('/xxx);

this.$router.replace('/xxx);

在入口组件App.vue中排版

<template>

  <div id="app">

    <!--    <img src="./assets/logo.png">-->

    <router-view/>

    <!-- /home这里的active-class的样式,覆盖了Vue自己的router-link-active样式 -->

    <router-link to="/home" tag="button" replace active-class="active">home.js</router-link>

    <router-link to="/page1" tag="li" replace>page1.js</router-link>

<!--    <router-link to="/page2" replace>page2.js</router-link>-->

    <button @click="page2Click">page2.js</button>

    <table>

      <tr>

        <td>测试router-view的排版布局</td>

        <td><router-view></router-view></td>

      </tr>

    </table>

  </div>

</template>

<script>

  export default {

    name: 'App',

    methods: {

      page2Click(){

        // this.$router.push('/page2');

        this.$router.replace('/page2');

      }

    }

  }

</script>

<style>

  .router-link-active {

    color: red;

  }

  .active {

    color:blue;

  }

</style>

8.动态路由

相比较于index.js文件,里面配置的“path”与“component”是明确一一对应的,但存在“path”不确定的情况,这种“path”与“component”匹配的关系,称为动态路由;

典型代表就是Restful风格的URL,例如,我做了一个用户列表/userlist/,如果我想查看具体某个人的信息则是类似这样的:/user/zhangsan、/user/lisi,这里就需要动态路由来完成;

动态路由的开发过程:在vue-router中配置动态path,其组成为:/xxx/:yyyy,这里的/xxx/就是path,而yyyy则是动态参数;然后就可以通过$route.params.yyyy来获取;

①定义动态路由

// 导入Vue

import Vue from 'vue'

// 导入路由

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

import home from '@/components/home'

import page1 from '@/components/page1'

import page2 from '@/components/page2'

import user from '@/components/User'

// 第一步:通过Vue.use(插件),来全局安装插件

Vue.use(Router)

// 第二步:创建路由对象

export default new Router({

  // 一次性全部修改完router中匹配后,router-link颜色

  linkActiveClass: 'active',

  // 从URL的hash模式,变成history模式

  mode: 'history',

  // 第三步:配置路由与组件之间的映射关系

  routes: [

    {

      // 路径名,首页这里默认就是/,这里注意下

      path: '/',

      // 重定向前端工程的首页,注意这里写的是path路径,而不是component的名字

      redirect: '/home'

    },

    {

      // 路径名

      path: '/home',

      // 该路径对应的组件

      component: home

    },{

      // 路径名

      path: '/page1',

      // 该路径对应的组件

      component: page1

    },

    {

      // 路径名

      path: '/page2',

      // 该路径对应的组件

      component: page2

    },

    {

      // 这里配置了动态路由

      path: '/user/:userid',

      // 该路径对应的组件

      component: user

    }

  ]

})

// 第四步:将路由router挂载到Vue实例中

// 第五步:<router-link/>显示链接和<router-view/>自定义组件显示位置

②在入口App.vue中定义动态path

<template>

  <div id="app">

    <!--    <img src="./assets/logo.png">-->

    <router-view/>

    <!-- /home这里的active-class的样式,覆盖了Vue自己的router-link-active样式 -->

    <router-link to="/home" tag="button" replace active-class="active">home.js</router-link>

    <router-link to="/page1" tag="li" replace>page1.js</router-link>

<!--    <router-link to="/page2" replace>page2.js</router-link>-->

    <button @click="page2Click">page2.js</button>

    <router-link v-bind:to="'/user/' + userid" tag="button" replace>zhangsan</router-link>

    <table>

      <tr>

        <td>测试router-view的排版布局</td>

        <td><router-view></router-view></td>

      </tr>

    </table>

  </div>

</template>

<script>

  export default {

    name: 'App',

    data(){

      return {

        // 这里模拟从后台服务器获取到的数据库里面的数据

        userid:'zhangsan'

      }

    },

    methods: {

      page2Click(){

        // this.$router.push('/page2');

        this.$router.replace('/page2');

      }

    }

  }

</script>

<style>

  .router-link-active {

    color: red;

  }

  .active {

    color:blue;

  }

</style>

③在具体页面User.vue获取传递过来的参数

<template>

  <div>

    <h2>userid:{{userid}}</h2>

    <h3>userid:{{$route.params.userid}}</h3>

  </div>

</template>

<script>

    export default {

        name: "User",

        computed:{

            userid(){

              // 注意:$router代表的是vue-router,而$route代表的是vue-router中的routes中匹配当前“path”的route

              return this.$route.params.userid;

            }

        }

    }

</script>

<style scoped>

</style>

9.vue-router打包与懒加载

按照CLI2的打包逻辑,其打包之后的目录结构如下:

我们会发现几个特点:

①与webpack相比,CLI2把js与css文件进行拆分,不再全部融合到一个bundle.js文件中;

②把bundle.js中的js代码分拆成3个不同的文件,每个文件承担一部分功能;

③app.xxx.js存放的是前端工程中的业务逻辑代码;

④manifest.xxx.js存放的是做底层支持的代码,例如:对于不支持做ES6的浏览器做支持处理等等,可以理解为针对不同浏览器做的底层兼容性处理;

⑤vendor.xxx.js则为第三方框架的代码,既然开发的时候用到了,那么,打包的时候就一起发布到服务器上;

但是,这种打包方式下会有一个问题:

按照CLI的约定,会把所有的业务代码全部打包到app.js文件中,这就导致一个app.js文件就非常大,当用户浏览器去加载这个文件的时候就变得非常慢,会导致用户浏览器会出现空白页;

为了解决这个问题,同时,考虑到vue-router是与URL进行绑定的,每个vue-router可以看做是一个业务模块的入口,因此,我们约定:由于一个vue-router就映射到一个组件,一个业务逻辑,因此,我们就把针对vue-router进行打包,一个vue-router打包成一个js文件,这样,当浏览器用户触发某个URL后,再让浏览器去加载该URL对应的vue-router.js文件;

那么怎么去配置CLI来按照vue-router来打包呢?

①结合Vue的异步组件、webpack打包:

const home = resolve =>{require.ensure([‘home.vue’],()=>{resolve(require(‘home.vue’))})}

②AMD写法:

const home = resovle -> require([‘home.vue’],resolve)

③在ES6中,使用import语法:

const home = () => import(‘home.vue’)

下面是自定义vue-router

// 导入Vue

import Vue from 'vue'

// 导入路由

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

// import home from '@/components/home'

// import page1 from '@/components/page1'

// import page2 from '@/components/page2'

// import user from '@/components/User'

// 第一步:通过Vue.use(插件),来全局安装插件

Vue.use(Router)

// 第二步:创建路由对象

export default new Router({

  // 一次性全部修改完router中匹配后,router-link颜色

  linkActiveClass: 'active',

  // 从URL的hash模式,变成history模式

  mode: 'history',

  // 第三步:配置路由与组件之间的映射关系

  routes: [

    {

      // 路径名,首页这里默认就是/,这里注意下

      path: '/',

      // 重定向前端工程的首页,注意这里写的是path路径,而不是component的名字

      redirect: '/home'

    },

    {

      // 路径名

      path: '/home',

      // 该路径对应的组件

      // component: home

      component: () => import('@/components/home')

    },{

      // 路径名

      path: '/page1',

      // 该路径对应的组件

      // component: page1

      component: () => import('@/components/page1')

    },

    {

      // 路径名

      path: '/page2',

      // 该路径对应的组件

      // component: page2

      component: () => import('@/components/page2')

    },

    {

      // 这里配置了动态路由

      path: '/user/:userid',

      // 该路径对应的组件

      // component: user

      component: () => import('@/components/User')

    }

  ]

})

// 第四步:将路由router挂载到Vue实例中

// 第五步:<router-link/>显示链接和<router-view/>自定义组件显示位置

打包之后的效果,正好4个import对应4个js文件:

10.vue-router嵌套路由

2个URL,例如:/home/info1、/home/info2,这样的话,这2个URL共同属于/home/,我希望我访问/home/info1时,分别渲染/home/和/home/info1;

开发步骤如下:

①创建对应的子组件,并且在vue-router中配置对应的子路由;

②在组件内部使用<router-view/>标签;

例如:

①分别创建2个子组件:info1和info2:

<template>

  <div>

    /home/info1

  </div>

</template>

<script>

    export default {

        name: "info1"

    }

</script>

<style scoped>

</style>

<template>

  <div>

    /home/info2

  </div>

</template>

<script>

    export default {

        name: "info1"

    }

</script>

<style scoped>

</style>

②在vue-router中配置对应的子路由:

// 导入Vue

import Vue from 'vue'

// 导入路由

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

// import home from '@/components/home'

// import page1 from '@/components/page1'

// import page2 from '@/components/page2'

// import user from '@/components/User'

// 第一步:通过Vue.use(插件),来全局安装插件

Vue.use(Router)

// 第二步:创建路由对象

export default new Router({

  // 一次性全部修改完router中匹配后,router-link颜色

  linkActiveClass: 'active',

  // 从URL的hash模式,变成history模式

  mode: 'history',

  // 第三步:配置路由与组件之间的映射关系

  routes: [

    {

      // 路径名,首页这里默认就是/,这里注意下

      path: '/',

      // 重定向前端工程的首页,注意这里写的是path路径,而不是component的名字

      redirect: '/home'

    },

    {

      // 路径名

      path: '/home',

      // 该路径对应的组件

      // component: home

      component: () => import('@/components/home'),

      children: [

        {

          // 这里配置/home的默认展示界面

          path: '',

          redirect: 'info1'

        },

        {

          // 这里的path不用"/"来作为开头

          path: 'info1',

          component: () => import('@/components/info1')

        },

        {

          // 这里的path不用"/"来作为开头

          path: 'info2',

          component: () => import('@/components/info2')

        }

      ]

    },{

      // 路径名

      path: '/page1',

      // 该路径对应的组件

      // component: page1

      component: () => import('@/components/page1')

    },

    {

      // 路径名

      path: '/page2',

      // 该路径对应的组件

      // component: page2

      component: () => import('@/components/page2')

    },

    {

      // 这里配置了动态路由

      path: '/user/:userid',

      // 该路径对应的组件

      // component: user

      component: () => import('@/components/User')

    }

  ]

})

// 第四步:将路由router挂载到Vue实例中

// 第五步:<router-link/>显示链接和<router-view/>自定义组件显示位置

③由于是在/home/下配置的info1和info2,因此,我们就需要在home.vue中配置info1和info2需要展示的位置,即在组件内部使用<router-view/>标签:

<template>

  <div>

    <h2>这是home页面</h2>

    <span>这里的to要用"/"来作为开头,还得用/home/开头,只能用绝对路径才行,因为相对路径的话,系统也不知道你会把系统路径放在哪里</span>

    <router-link to="/home/info1">info1</router-link>

    <router-link to="/home/info2">info2</router-link>

    <router-view></router-view>

  </div>

</template>

<script>

    export default {

        name: "home"

    }

</script>

<style scoped>

</style>

11.vue-router传递参数

vue-router传递参数,根据参数位置不同,大致分为2种类型:

传递方式

配置路由格式

传递的方式

传递后形成的路径

params类型

/router/:id

在path后面跟上对应的值

/user/zhangsan

/user/lisi

query类型

/router,也就是普通配置

对象中使用query的key作为传递方式

/user?name=wangsu

/user?name=lisi

下面我们介绍下“query”类型是如何传递参数的:

①定义query1.vue

<template>

    <div>

      <h2>测试query类型的传递方式</h2>

    </div>

</template>

<script>

    export default {

        name: "ParamTest"

    }

</script>

<style scoped>

</style>

②以配置普通vue-router方式来配置该path

// 导入Vue

import Vue from 'vue'

// 导入路由

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

// import home from '@/components/home'

// import page1 from '@/components/page1'

// import page2 from '@/components/page2'

// import user from '@/components/User'

const query1 = () => import('../components/query1')

// 第一步:通过Vue.use(插件),来全局安装插件

Vue.use(Router)

// 第二步:创建路由对象

export default new Router({

  // 一次性全部修改完router中匹配后,router-link颜色

  linkActiveClass: 'active',

  // 从URL的hash模式,变成history模式

  mode: 'history',

  // 第三步:配置路由与组件之间的映射关系

  routes: [

    {

      // 路径名,首页这里默认就是/,这里注意下

      path: '/',

      // 重定向前端工程的首页,注意这里写的是path路径,而不是component的名字

      redirect: '/home'

    },

    {

      // 路径名

      path: '/home',

      // 该路径对应的组件

      // component: home

      component: () => import('@/components/home'),

      children: [

        {

          // 这里配置/home的默认展示界面

          path: '',

          redirect: 'info1'

        },

        {

          // 这里的path不用"/"来作为开头

          path: 'info1',

          component: () => import('@/components/info1')

        },

        {

          // 这里的path不用"/"来作为开头

          path: 'info2',

          component: () => import('@/components/info2')

        }

      ]

    },{

      // 路径名

      path: '/page1',

      // 该路径对应的组件

      // component: page1

      component: () => import('@/components/page1')

    },

    {

      // 路径名

      path: '/page2',

      // 该路径对应的组件

      // component: page2

      component: () => import('@/components/page2')

    },

    {

      // 这里配置了动态路由

      path: '/user/:userid',

      // 该路径对应的组件

      // component: user

      component: () => import('@/components/User')

    },

    {

      // query方式的path

      path: '/querytest',

      component: query1

    }

  ]

})

// 第四步:将路由router挂载到Vue实例中

// 第五步:<router-link/>显示链接和<router-view/>自定义组件显示位置

③在入口App.vue中配置该path

<template>

  <div id="app">

    <!--    <img src="./assets/logo.png">-->

    <!-- /home这里的active-class的样式,覆盖了Vue自己的router-link-active样式 -->

    <router-link to="/home" tag="button" replace active-class="active">home.js</router-link>

    <router-link to="/page1" tag="li" replace>page1.js</router-link>

<!--    <router-link to="/page2" replace>page2.js</router-link>-->

    <button @click="page2Click">page2.js</button>

    <router-link v-bind:to="{path:'/querytest',query: {name:'wangwu',age:18}}" >测试query</router-link>

    <router-link v-bind:to="'/user/' + userid" tag="button" replace>zhangsan</router-link>

    <br/>

    <div>

      <router-view></router-view>

    </div>

<!--    <table>-->

<!--      <tr>-->

<!--        <td>测试router-view的排版布局</td>-->

<!--        <td><router-view></router-view></td>-->

<!--      </tr>-->

<!--    </table>-->

  </div>

</template>

<script>

  export default {

    name: 'App',

    data(){

      return {

        // 这里模拟从后台服务器获取到的数据库里面的数据

        userid:'zhangsan'

      }

    },

    methods: {

      page2Click(){

        // this.$router.push('/page2');

        this.$router.replace('/page2');

      }

    }

  }

</script>

<style>

  .router-link-active {

    color: red;

  }

  .active {

    color:blue;

  }

</style>

④修改query1.vue,获取传递过来的参数,此刻URL则变成这样了:http://localhost:8080/querytest?name=wangwu&age=18

<template>

    <div>

      <h2>测试query类型的传递方式</h2>

      <h2>{{$route.query}}</h2>

      <h2>{{$route.query.name}}</h2>

      <h2>{{$route.query.age}}</h2>

    </div>

</template>

<script>

    export default {

        name: "ParamTest"

    }

</script>

<style scoped>

</style>

⑤把入口App.vue中配置的path,改成click事件,从代码来触发请求

<template>

  <div id="app">

    <!--    <img src="./assets/logo.png">-->

    <!-- /home这里的active-class的样式,覆盖了Vue自己的router-link-active样式 -->

    <router-link to="/home" tag="button" replace active-class="active">home.js</router-link>

    <router-link to="/page1" tag="li" replace>page1.js</router-link>

<!--    <router-link to="/page2" replace>page2.js</router-link>-->

    <button @click="page2Click">page2.js</button>

    <router-link v-bind:to="'/user/' + userid" tag="button" replace>zhangsan</router-link>

<!--    这里使用v-bind:to只是为了让vue来解析{},而不是让vue-router把{}当做普通字符串来处理-->

    <router-link v-bind:to="{path:'/querytest',query: {name:'wangwu',age:18}}" >测试query</router-link>

    <button @click="queryClick">测试query</button>

    <br/>

    <div>

      <router-view></router-view>

    </div>

<!--    <table>-->

<!--      <tr>-->

<!--        <td>测试router-view的排版布局</td>-->

<!--        <td><router-view></router-view></td>-->

<!--      </tr>-->

<!--    </table>-->

  </div>

</template>

<script>

  export default {

    name: 'App',

    data(){

      return {

        // 这里模拟从后台服务器获取到的数据库里面的数据

        userid:'zhangsan'

      }

    },

    methods: {

      page2Click(){

        // this.$router.push('/page2');

        this.$router.replace('/page2');

      },

      queryClick(){

        this.$router.push({path:'/querytest',query: {name:'wangwu',age:18}});

      }

    }

  }

</script>

<style>

  .router-link-active {

    color: red;

  }

  .active {

    color:blue;

  }

</style>

12.vue-router全局导航守卫

导航栏,这个效果就不多说了,类似于下面这种效果:

这里以如何添加SPA上的title为例进行说明,至于开发步骤呢(这里针对的是全局导航),分成2个步骤:

①在各个子模块中添加meta属性,来为自己的模块提供title来供全局导航来使用;

②在入口main.js中,为全局router配置全局导航守卫,下面是具体代码范例:

为各个子模块添加title数据:

// 导入Vue

import Vue from 'vue'

// 导入路由

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

// import home from '@/components/home'

// import page1 from '@/components/page1'

// import page2 from '@/components/page2'

// import user from '@/components/User'

const query1 = () => import('../components/query1')

// 第一步:通过Vue.use(插件),来全局安装插件

Vue.use(Router)

// 第二步:创建路由对象

export default new Router({

  // 一次性全部修改完router中匹配后,router-link颜色

  linkActiveClass: 'active',

  // 从URL的hash模式,变成history模式

  mode: 'history',

  // 第三步:配置路由与组件之间的映射关系

  routes: [

    {

      // 路径名,首页这里默认就是/,这里注意下

      path: '/',

      // 重定向前端工程的首页,注意这里写的是path路径,而不是component的名字

      redirect: '/home'

    },

    {

      // 路径名

      path: '/home',

      // 该路径对应的组件

      // component: home

      component: () => import('@/components/home'),

      // 这里的meta是元数据的意思,可以存放很多自定义的,关于本模块的个性化数据

      meta: {

        title: 'home标题'

      },

      children: [

        {

          // 这里配置/home的默认展示界面

          path: '',

          redirect: 'info1'

        },

        {

          // 这里的path不用"/"来作为开头

          path: 'info1',

          component: () => import('@/components/info1')

        },

        {

          // 这里的path不用"/"来作为开头

          path: 'info2',

          component: () => import('@/components/info2')

        }

      ]

    },{

      // 路径名

      path: '/page1',

      // 该路径对应的组件

      // component: page1

      component: () => import('@/components/page1'),

      // 这里的meta是元数据的意思,可以存放很多自定义的,关于本模块的个性化数据

      meta: {

        title: 'page1标题'

      }

    },

    {

      // 路径名

      path: '/page2',

      // 该路径对应的组件

      // component: page2

      component: () => import('@/components/page2'),

      // 这里的meta是元数据的意思,可以存放很多自定义的,关于本模块的个性化数据

      meta: {

        title: 'page2标题'

      }

    },

    {

      // 这里配置了动态路由

      path: '/user/:userid',

      // 该路径对应的组件

      // component: user

      component: () => import('@/components/User'),

      // 这里的meta是元数据的意思,可以存放很多自定义的,关于本模块的个性化数据

      meta: {

        title: '动态路由params标题'

      }

    },

    {

      // query方式的path

      path: '/querytest',

      component: query1,

      // 这里的meta是元数据的意思,可以存放很多自定义的,关于本模块的个性化数据

      meta: {

        title: 'query标题'

      }

    }

  ]

})

// 第四步:将路由router挂载到Vue实例中

// 第五步:<router-link/>显示链接和<router-view/>自定义组件显示位置

在入口main.js中,为router配置全局导航守卫

import Vue from 'vue'

import App from './App'

// 这里并没有详细说出/router/index.js文件,那是因为Vue默认找的就是该目录下名字叫index的js文件

// 第四步:将路由router挂载到Vue实例中

import router from './router'

Vue.config.productionTip = false

/* eslint-disable no-new */

new Vue({

  el: '#app',

  // 第四步:将路由router挂载到Vue实例中

  router,

  render: h => h(App)

})

// 前置钩子函数,跳转之前进行回调

router.beforeEach(function (to,from,next) {

  console.log('-----------beforeEach------');

  // 这种调用方法存在一个问题:路由嵌套,当嵌套路由作为一个前端工程首页的时候,并没有显示title

  // document.title = to.meta.title;

  // console.log(to)

  // 通过F12查看,嵌套路由to的meta属性中并没有title属性,反而在matched数组中的[0]的meta属性中存在title属性

  // 其他普通路由里的meta属性与matched数组的[0]中均有title属性,所以,这里改进为从matched数组中获取

  document.title = to.matched[0].meta.title;

  // 必须手动调用next,否则router导航没有任何点击效果

  next();

});

// 后置钩子函数,即跳转完毕后再去回调

router.afterEach(function (to, from) {

  console.log('-----------afterEach------');

});

这里解释一下为什么不能使用to.meta.title的原因,看下图:

遇到嵌套路由的时候,跳到首页时会出现这种情况,然后通过F12查看发现:

所以,这里改用matched数组的形式;

12.1.扩展

①如果是后置钩子函数,即afterEach,就不需要主动调用next()函数

②前置钩子函数称为全局守卫,同时根据定义位置的不同,还分别存在:路由独享的守卫,组件内的守卫;

下面是官方说明:

13.keep-alive与vue-router

由于vue-router是一个组件,如果直接被包裹在keep-alive里面,那么所有路径匹配到的视图组件都会被缓存起来;

keep-alive也是Vue内置的一个组件,可以使得被包含的组件保留其状态,来避免重新渲染;

<keep-alive>

      <router-view></router-view>

    </keep-alive>

注意:

①当使用keep-alive来包裹组件的时候,该组件的activated、deactivated才会生效起作用,否则不会起作用;

②<keep-alive/>有2个属性:include和exclude,exclude用来说明哪个vue-route不会被缓存,而include则是说明哪个vue-router会被缓存,取值来自自定义组件的name属性值,例如:

<template>

  <div>

    <h2>这是page1页面</h2>

  </div>

</template>

<script>

    export default {

        name: "page1"

    }

</script>

<style scoped>

</style>

<template>

  <div id="app">

    <!--    <img src="./assets/logo.png">-->

    <!-- /home这里的active-class的样式,覆盖了Vue自己的router-link-active样式 -->

    <router-link to="/home" tag="button" replace active-class="active">home.js</router-link>

    <router-link to="/page1" tag="li" replace>page1.js</router-link>

<!--    <router-link to="/page2" replace>page2.js</router-link>-->

    <button @click="page2Click">page2.js</button>

    <router-link v-bind:to="'/user/' + userid" tag="button" replace>zhangsan</router-link>

<!--    这里使用v-bind:to只是为了让vue来解析{},而不是让vue-router把{}当做普通字符串来处理-->

    <router-link v-bind:to="{path:'/querytest',query: {name:'wangwu',age:18}}" >测试query</router-link>

    <button @click="queryClick">测试query</button>

    <br/>

<!--    该属性里面不要加空格-->

    <keep-alive exclude="page1,User">

      <router-view></router-view>

    </keep-alive>

<!--    <table>-->

<!--      <tr>-->

<!--        <td>测试router-view的排版布局</td>-->

<!--        <td><router-view></router-view></td>-->

<!--      </tr>-->

<!--    </table>-->

  </div>

</template>

14.扩展:Vue Router官网读后感

14.1.Vue Router与Vue关系

①是Vue.js官方的路由管理器;

②Vue Router是Vue.js(opens new window)官方的路由管理器。它和Vue.js的核心深度集成,让构建单页面应用变得易如反掌;

14.2.官方链接

https://router.vuejs.org/zh/installation.html

14.3.官方案例

<script src="https://unpkg.com/vue/dist/vue.js"></script>

<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">

  <h1>Hello App!</h1>

  <p>

    <!-- 使用 router-link 组件来导航. -->

    <!-- 通过传入 `to` 属性指定链接. -->

    <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->

    <router-link to="/foo">Go to Foo</router-link>

    <router-link to="/bar">Go to Bar</router-link>

  </p>

  <!-- 路由出口 -->

  <!-- 路由匹配到的组件将渲染在这里 -->

  <router-view></router-view>

</div>

// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义 (路由) 组件。

// 可以从其他文件 import 进来

const Foo = { template: '<div>foo</div>' }

const Bar = { template: '<div>bar</div>' }

// 2. 定义路由

// 每个路由应该映射一个组件。 其中"component" 可以是

// 通过 Vue.extend() 创建的组件构造器,

// 或者,只是一个组件配置对象。

// 我们晚点再讨论嵌套路由。

const routes = [

  { path: '/foo', component: Foo },

  { path: '/bar', component: Bar }

]

// 3. 创建 router 实例,然后传 `routes` 配置

// 你还可以传别的配置参数, 不过先这么简单着吧。

const router = new VueRouter({

  routes // (缩写) 相当于 routes: routes

})

// 4. 创建和挂载根实例。

// 记得要通过 router 配置参数注入路由,

// 从而让整个应用都有路由功能

const app = new Vue({

  router

}).$mount('#app')

// 现在,应用已经启动了!

// Home.vue

export default {

  computed: {

    username() {

      // 我们很快就会看到 `params` 是什么

      return this.$route.params.username

    }

  },

  methods: {

    goBack() {

      window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')

    }

  }

}

14.4.案例总结

①<router-link/>类似于一个导航栏;

②<router-view/>现实自定义组件内容;

③通过注入路由器,我们可以在任何组件内通过 this.$router 访问路由器,也可以通过 this.$route 访问当前路由;

14.5.动态路由匹配

14.5.1.为什么会出现这个知识点?

按照RestFul风格,其中的GET请求很多都是下面形式,例如:

①/user/zhangsan

②/user/lisi

为了解决这类问题,出现了“动态路径参数”的技术,用该技术可以整合这类URL,例如:/user/:id,动态路径参数都是以冒号开头

一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到this.$route.params,可以在每个组件内使用。于是,我们可以更新模板,输出当前用户的ID:$route.params.id

14.5.2.为什么会出现这个知识点:响应路由参数的变化

当我从/user/foo导航到/user/bar时,为了提高效率,vue-router旧组件会被复用,这就导致组件的生命周期函数无法被回调;

但是,我就是想在导航切换的时候,去回调组件的生命周期,该如何解决呢,解决方法有2个:

①用watch(监测变化)$route对象;

②使用2.2中引入的beforeRouteUpdate导航守卫;

14.5.3.为什么会出现这个知识点:捕获所有路由或404 Not found路由

常规参数只会匹配被/分隔的URL片段中的字符,但如果想匹配任意路径,我们可以使用通配符(*);

当我们在vue文件中,定义路由时,特别是使用*通配符路由时,需要把*通配符路由放在最后,其他具体路由则放在前面;

*通配符路由通常用于客户端404错误,就是避免那些别有用心的人去探测应用的URL路径信息;

注意:当使用一个通配符时,$route.params内会自动添加一个名为pathMatch参数,它包含了URL通过通配符被匹配的部分;

14.5.4.为什么会出现这个知识点:高级匹配模式

vue-router使用的是path-to-regexp来作为路径匹配引擎,因此,如果目前信息无法满足你的要求,则可以通过这个官网去了解一下高级用法;

14.5.5.为什么会出现这个知识点:匹配优先级

有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:路由定义得越早,优先级就越高

14.6.编程式的导航

14.6.1.router.push

<router-link>内部是通过router.push方法来实现内部逻辑的,那么,换而言之,我们可以通过router.push来实现编程式的导航;

①方法定义如下:router.push(location, onComplete?, onAbort?)

②在Vue实例内部,可以通过$router访问路由实例;

③想要导航到不同的URL,则使用router.push方法。这个方法会向history栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的URL;

注意:

①path与params不能共存,如果提供了path,params会被忽略,所以,要么是手写完整的带有参数的 path,要么是name + params组合;

②在2.2.0+,可选的在router.push或router.replace中提供 onComplete和onAbort回调作为第二个和第三个参数,其中onComplete将会在导航成功完成(在所有的异步钩子被解析之后) 时调用,onAbort将在终止(导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由)时调用

③在3.1.0+,可以省略第二个和第三个参数,此时如果支持 Promise,router.push或 router.replace将返回一个 Promise;

④如果目的地和当前路由相同,只有参数发生了改变(比如从一个用户资料到另一个/users/1->/users/2),需要使用 beforeRouteUpdate来响应这个变化;

14.6.2.router.replace

跟router.push很像,唯一的不同就是,它不会向history添加新记录,而是替换掉当前的history记录;

14.6.3.router.go

这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n);

14.6.4.操作History

router.push、 router.replace和router.go跟 window.history.pushState、window.history.replaceState和window.history.go 好像, 实际上它们确实是效仿window.history API;

还有值得提及的,Vue Router 的导航方法(push、 replace、 go)在各类路由模式(history、 hash 和 abstract)下表现一致;

14.7.命名路由

通俗来讲,就是给路由对象起一个别名,通过name属性,给这个router实例起个别名;

14.7.1.方式1:$route.params.XXX

这种方式的缺点在于让组件只能在某些特定的URL上使用;

14.8.命名视图

同一个层级内,同时展示多个视图,通过router实例的components属性来指定多个子组件,然后再通过<router-view name="XXXX"/>来引用;

14.9.重定向和别名

重定向也是通过router实例的routes配置来完成,通过routes是一个数组,数组元素的redirect属性来指定重定向地址;

redirect属性取值可以是字符串,也可以是一个命名的路由,甚至是一个方法来动态返回重定向目标;

而别名是指什么呢?

{ path: '/a', redirect: '/b' }这里,当我在浏览器中输入/a的时候,vue-router会把/a转换成/b,然后拿着/b去查找匹配的路由;

{ path: '/a', component: A, alias: '/b' }而别名的是这样的:当我在浏览器输入/b,但这个/b的本体是/a,所以,vue-router却是按照/a去匹配的路由,相当于/a有2种访问方式:/a、/b;

14.10.路由组件传参

一个是VueRouter实例,一个是Vue实例,那么,VueRouter实例是如何向Vue实例传递参数的呢?

14.10.1.方式2:props解耦

在Vue实例中通过props属性定义要传递的参数,在VueRouter实例中为每个使用的组件中都添加props属性:

①当props被设置为true,route.params将会被设置为组件属性;

②当props是一个对象,它会被按原样设置为组件属性,当 props是静态的时候有用;

③当props是一个函数,这样便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等;

请尽可能保持props函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义props需使用包装组件,这样 Vue 才可以对状态变化做出反应。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值