Vue组件和路由

Vue组件

我们通过将网页中的内容拆分为小的组件,这些组件可以独立的完成某些功能,当我们在其他地方需要同样或者相似功能时候,使用对应的组件即可。组件可以大大的提高我们 代码的复用率,提高开发效率。

创建组件

在Vue 中,我们可以自己创建组件对象,组件同一个页面一样,有自己的html 标签,自己的 css 样式,和自己的 js 来处理逻辑。Vue提供了以下的方式来创建组件。

定义组件

定义全局组件是在Vue类上调用extend 和 component 方法创建组件(第一种方式),然而 Vue 提供了语法糖,可以不使用 extend 而直接使用一个对象创建(第二种方式)

// 第一种方式,定义组件
<script>
var comp1 = Vue.extend({
  template: "<p> 这是一个组件的html内容 </p>",
  data:function(){
      return {}
  }
})
Vue.compomnet("myComp1", comp1) // myComp1 为组件的名字,直接将该名字的标签放入页面的元素中即可


// 第二种方式,直接使用一个对象
Vue.compoment("组件名字", { 
      template: "<p> 这是一个组件的html内容 </p>",
      data:function(){ return {} },
    })
</script>

// 按照组件名 使用组件
<div>
    <my-comp1> </my-comp1>   // 名字驼峰命名 需要小写,并使用 - 分割
</div>

调用 component方法,传入一个指定的对象来创建组件,这个对象常用的几个key如下

  • template:一个html 格式的字符串,必须只有一个根元素。同时内部也支持Vue中的插值表达式,各种指令等。
  • data:同Vue 中data 相同,这是组件存放自己数据的地方,但必须是一个function,其返回值会作为组件的data 对象,所以返回值是一个对象。使用data 是函数原因是因为组件可能被多次重复使用,这样每次使用时,都会调用该函数返回一个自己的 data 对象,不会造成多个标签的数据相互混乱。(如果不使用函数返回data 而是直接使用data 对象,那么这个组件在被使用多次时候,实际的data 只有一个,每个组件的data 引用同一个 对象,他们的展示的值也是完全相同的,并且,当修改一个组件中的内容,其他组件也会相应的改变,因为他们都是共用的同一个data)
  • methods:定义函数处理自己内部的逻辑。
  • 组件使用 component 方法注册到Vue对象中后才可以使用。使用时像上述方式直接添加以组件名字相同的标签即可,或者使用其他标签,添加一个 is=组件名 的属性即可,该标签将会被渲染为这个名字的组件。

抽离template

为了方便编辑,可以将template代码从对象中抽离出来,使用一个template单独定义,而后使用选择器绑定到template上

<template id="app"> 
    <div>
      这是一个组建template内容
  </div>
</template>

<script>
var comp = Vue.extend({
    template:"#app",             //  id选择器绑定template的内容
    data:function(){
    return {}
  }
})

</script>

组件的组织

一个网页是由 许许多多的组件组成的,每个区域完成自己的功能,许许多多的组件就需要很好组织他们。从官网的图示可以很好的理解组件是如何组织的。

组件可以并列使用,也可以嵌套使用,最终形成了这个样的一棵树形结构,也就形成了父子组件的关系,同时。

另外,组件还应该能够被方便的替换,选择不同的条件时,可以选择性的在指定的位

父子组件

简单定义一个父子组件

在一个组件中使用使用另一个组件。

var comp1 = Vue.extend({
  template: "<div> 
          这是父组件
          <my-child > </my-child>
        </div>
", data:function(){ return {} } }) var comp2 = Vue.extend({ template: "<div> 这是子组件 </div>", data:function(){ return {} } }) Vue.component("my-child", comp2) Vue.component("my-parent", comp1)

这里注册了两个组件,comp1 中 嵌套了组件comp2,comp1 是 comp2 的父组件,当我们使用父组件时,父组件中的子组件也会被渲染,并属于父组件的一部分。

使用这种方式,子组件在父组件中使用方式,这个父组件只能渲染这个组件,更优雅的方式是使用动态组件的方式,也就是 is 属性,该一个标签添加 is 属性,这个标签将会被替换为该名字的组件。

var comp1 = Vue.extend({
      template: "<div> 这是父组件 <p :is='childName'> </p> </div>",
      data: function () {
        return {
          childName:"my-child"
        }
      }
    })

    var comp2 = Vue.extend({
      template: "<div> 这是子组件 </div>",
      data: function () {
        return {}
      },
    })
Vue.component("my-child", comp2)
Vue.component("my-parent", comp1)

这样通过改变 父组件data中的 childName属性值该指定子组件的名字,从而渲染指定的组件。

父子组件传值

在子组件中,无法直接访问父组件中的任何数据,如果需要使用父组件的数据,需要父组件传递给子组件,并且子组件需要在props中指定相同的变量名进行接收。

<parent>
    <child v-bind:parentMsg="msg"></child>
</parent>

var comp2 = Vue.extend({
  template: "<div> 这是子组件 </div>",
  data: function () {
          return {}
    },
  props:["parentMsg"]
  })
Vue.component("my-child", comp2)

msg 是父组件中的变量,通过在子组件标签中定义一个parentMsg 的属性接收这个msg的值,在子组件中,props 中定义了一个 parentMsg 的变量名字,这是必须定义的,否则子组件仍然无法使用 标签中的parentMsg 这个属性值,并且两个变量名必须相同,子组件才能够对应的接收这些传入的值,所有的传入的值,都在子组件的 $prosp属性中,也可以直接使用 this.parentMsg 访问,这是提供的一种便捷方式。

传入的数据可以被子组件任意的使用,但是如果传入的是引用类型的数据,例如数组和对象,子组件最好不要在这个原值上对这个数据或者对象内部数据进行修改,这样修改会造成父组件中的数据被同步的修改,如果要进行修改,应该进行深拷贝,对副本进行修改。这样做为了保证数据的单项流动,也就是从父组件向子组件进行传递,父组件数据 的更新会向下流动到子组件中,但是反过来则不行。因为子组件接收的父组件的数据可能还被父组件传递给了其他的子组件,如果该子组件更改了这个数据,其他子组件的数据也会被更改,而各个子组件应该是相互独立的,这样通常会造成意外的结果,并使得数据流动变得难以理解。 

props 验证

var comp2 = Vue.extend({
  template: "<div> 这是子组件 </div>",
  props:["parentMsg"]
  })
Vue.component("my-child", comp2)

我们定义了子组件可以接收父组件的数据,这个数据会根据变量名进行对应的接收,子组件在接收的同时,是可以在prosp 对数据进行验证,只需要向下面这样定义验证的规则即可。

var comp2 = Vue.extend({
  template: "<div> 这是子组件 </div>",
  props:{
    propA: Number,            // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propB: [String, Number],  // 多个可能的类型
    
    propC: {
      type: String,     // 字符串
      required: true    // 必填
    },
    propD: {
      type: Number,
      default: 100     // 默认为100
    },
    propE: {
      type: Object,
      // 默认值为 对象或数组 都必须从一个工厂函数获取,原理同data需要 函数返回相同
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数,返回true 通过,否则验证失败
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
  })

Vue.component("my-child", comp2)

类型检查可以使用基本的几种基本数据类型,也可以是自定义的类型,我们可以自定义构造函数,也就是自定义类型,作为type。

为组件定义事件

使用组件时,我们可以为组件绑定自定义的事件,使用 v-on 指令即可

<my-component v-on:my-event="doSomething"></my-component>

这个事件的处理函数是一个dosomething 的函数,这个函数是由组件外部定义的,传入到组件内部使用,组件内部根据这个 事件名在props 中定义一个同名字符串来接收这个事件。想要调用这个事件则需要 emit 函数进行调用。

例如我们可以将父组件的一个方法传入子组件中,并在子组件中调用这个方法。

<script>
  // 父组件,定义了一个show函数,并将这个show函数传递给子组件
  var comp1 = Vue.extend({
      template: "<parent>
                   <child v-bind:func="show"></child>    // 传递show函数,子组件使用func变量接收。
                 </parent>",
      data: function () {
        return {}
      }
      // 父组件中定义的show方法,我们要把他传递给子组件,子组件得到后调用他,并可以传入参数
      methods:{
          show(data){  // 这个函数属于父组件,传入子组件后,子组件通过 $emit()调用时,这个函数内部的 this 是父组件对象而不是子组件
            console.log(data) 
            console.log(this.data)   // 得到了子组件的数据
            this.data = data     
          }
      }
    }) 
  Vue.component("my-parent", comp1)
  
  //  定义一个子组件,并定义一个tocall方法,在方法中调用传入的函数
  var comp2 = Vue.extend({
  template: "<div> 这是子组件 </div>",
  data: function () {
          return {}
    },
  methods:{
      toCall(){   // 定义一个方法,在这个方法中调用父组件传入的func 引用的这个函数
        this.$emit("func", "参数1", "参数2")   // 函数名,参数
    }
  }
  props:["func"]      
  })
Vue.component("my-child", comp2)
</script>

子组件使用 func 这个标识符接受了父组件的 show 函数,然后使用 this.$emit()的方式调用这个函数,func 这个标识不会作为变量使用,所以不用遵循变量的定义规则,并且这个标识应该是全小写,即使在html中使用大写进行接收,组件内部也只能访问到全小写的标识。如果父组件函数有参数,子组件可以将自己的数据作为参数在调用父组件的函数,而这个函数内部的 this 是 父组件对象,这样父组件对象可以通过获取参数中的数据,得到子组件中的值,这也是一种子组件向父组件传值的方式。

组件切换

组件切换也是一种必要的组织组件的方式,我们遇到过登录注册功能,登录时可以切换到注册组件,反之亦可,在同一个位置上,通过选择的条件不同,渲染不同的组件。

实现方式 

有多种方式实现组件的切换

  • 一般的方式是使用 v-if 和 v-else 条件判断的方式选择性渲染。
  • 使用组件的 is 属性,动态的改变 is 的属性值来改变组件,上面的例子中已经出现过了,方法如下。
    • 父组件data 中保存当前渲染的 组件名字,页面中使用 is = “组件名字” 渲染该组件
    • 改变data 中 当前渲染组件的名字,is 属性对应的值也相继改变,从而组件切换
// 点击按钮切换,设置不同view值即可
<a href="" @click="view='v-a'"> 切换到a组件</a>
<a href="" @click="view='v-b'"> 切换到b组件</a>

<transition name="component-fade" mode="out-in">
  // 这个标签,:is 表示显示该名字的组件
  <component :is="view"></component>
</transition>

var = new Vue({
    data:{
        view:"v-a"
  }
    componments:{
        "v-a":{
            template:"<h1></h1>",
        },
        "v-b":{
            template:"<h1></h1>",
        }
    }
})
  • 最后一种方式则是使用 Vue 中提供的路由。

缓存切换组件

当多个组件进行切换显示时候,实际上只会单独的存在一个组件对象,当需要切换到其他的组件时,会立刻创建一个新的组件进行显示,同时删除掉当前显示的组件对象。大多数情况下我们的需求都是这样的,每次切换获得全新的组件。但是某些情况我们不希望被切换走的组件被删除,而是下次可以使用,这就需要将这个组件 进行缓存,缓存后,下次再次展示这个组件时,会使用缓存的组件。默认情况下不会对组件进行缓存,如果需要缓存组件只需要我们指定一个 <keep-alive> 标签 

<keep-alive >
  <component :is="view"></component>
</keep-alive>

data : () => {
  return {
    view: "comp1"
  }
}

is属性绑定了view变量,该变量值为comp1,则当前展示则为comp1组件,当view 变量指定的组件名字改变,组件将会发生组件切换,而被切换的组件comp1 就会被缓存起来。这样内存中实际上共存着这几个组件对象,而同一时刻只会有一个组件被渲染。被渲染的组件状态为 active,而没有渲染的组件为 deactive,组件的切换会改变组件的 这两种状态,组件active 和 deactive 两种状态进行切换时候,会触发两个生命周期函数,actived 和 deactived 函数,如果需要可以定义这两个钩子函数的内容。

使用<keep-alive> 缓存组件时需要注意以下几点:

  • 在<keep-alive> 内部进行切换组件,每个组件都需要有自己的名字,首先会检查组件的name 属性,也支持使用 vue.component("name", comp) 的方式注册的组件,但不能是没有注册的匿名组件。
  • 代码中的<keep-alive>标签只是一个缓存的标识,真实的页面的不会出现在这个标签。
  • <keep-alive> 内部只可能同时渲染展示一个根组件,其余均为 deactive 状态。
  • <keep-alive> 下的使用 v-for 展示的内容不会被缓存。
keep-alive的常用参数

使用 keep-alive 标签时候,可以添加几个属性。

  • include 和 exclude:指定被缓存的组件满足的条件,例如只想缓存部分组件,或者排除部分组件。可以用逗号分隔字符串、正则表达式或一个数组。匹配的是组建的 name 或者组件局部注册的名称,匿名组件不能被匹配。
<!-- 逗号分隔字符串 -->
// 只会缓存 a,b两个组件,其余组件不会缓存
<keep-alive include="a,b">  
  <component :is="view"></component>
</keep-alive>

<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
  <component :is="view"></component>
</keep-alive>

<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
  <component :is="view"></component>
</keep-alive>
  • max:指定缓存的组建的最大数量,一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。
<keep-alive :max="10">       // 最多缓存10个
  <component :is="view"></component>
</keep-alive>

路由 vue-router

简介

这里的路由是指前端的路由,也就是通过url 的不同来 选择性的渲染不同的组件。url 的改变只会让组件的进行切换,而不是从后端获取新的页面。

Vue 的前端路由主要是基于hash 来实现的,也就是url 上 # 标识符之后的部分。例如`http://vue-router/zh/#/user`这样一个url,#标识符后的路径为/user,我们可以设置规则匹配这个路径,使其对应的渲染指定的组件。

导包顺序

同样的,vue-router基于vue对象,所以必须在Vue包之后导入,确保当前环境中已经存在Vue对象。

直接在html中引入

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

使用webpack 工程化项目时的导入方式

import Vue from 'vue'               // 导入vue
import Router from 'vue-router'     // 导入vue-router

路由器对象

路由就是想要匹配当前访问的url,然后根据当前的 url 选择渲染对应的组件。于是我们需要一个url路径和组件之间的映射关系, vue中在router对象中定义映射关系,即路由器对象

var router = new VueRouter({     // 一个路由对象,内部配置 路径映射关系
  routes:[
    { path: '/foo', component: Foo },        // path 指定路径,component指定对应的组件的对象。
    { path: '/bar', component: Bar }
    ]
})

// 定义好路由器后,还需要将其与对应的Vue对象进行绑定,Vue中的配置对象中有对应的router这个key来绑定路由器。
var vm = new Vue({
    el:"#app",
   data:{ },
    methods:{
      show(){
        console.log(this.$router)
        console.log(this.$route)
      }
    },
    router: router,       // 绑定到 Vue 对象上,这个 router 只能在 #app 这个节点内部使用。
})

通过向Vue对象注入路由器,我们可以在Vue对象已经路由对象中使用的任何组件内通过 this.$router 访问路由器对象,获得内部的配置信息,也可以通过 this.$route 访问当前匹配到的路由。上面的方法中尝试去获取这个路由。

挂载到Vue上的路由器,会自动根据当前url中hash指定的 路径匹配到对应的组件,并将这个组件用于展示。但是我们并未向vue说明,我们要在哪里展示匹配到的组件,这就需要一个标记,在这个vue管理的 dom 节点中使用一个router-view标签即可。

<router-view> 标签

可以直接在页面中添加<router-view> 标签,vue 解析该标签时,会在自己对象上找到路由器对象,根据当前的url 匹配到组件,然后把这个组件渲染到这个标签的位置,这就是 <router-view> 标签的作用。

<div id="app">
    <p> 这是app </p>
  <router-view> </router-view>      // 当前url 匹配到的组件将展示到这个位置
  <router-view> </router-view>      // 又有一个该标签,则会展示一次,两个组件完全一样
</div>

<script>
var router = new VueRouter({     // 一个路由对象,内部配置 路径映射关系
  routes:[
      { path: '/foo', component: Foo },    
      { path: '/bar', component: Bar }
    ]
})

var vm = new Vue({
  el:"#app",
  data:{},
  methods:{},
  router: router,     // 绑定到Vue对象上,这个router 只能子 #app 这个节点内部使用。
})
</script>

在html 中凡是有 <router-view> 标签的地方,都会使用vue 对象中绑定的路由器匹配到的组件进行填充,所以上面使用了两次router-view,则会显示两个 匹配到的组件。

命名组件

实际上,一个路径可以匹配到的组件可以不止一个,可以有很多个,当匹配到多个组件时需要对每一个组件命名进行区分,使用时按照组件的名字将组件渲染到指定的位置。而用来渲染的组件的 router-view 组件也有对应的 name 属性来指定该标签只会渲染该名字的组件。

<div id="app">
    <p> 这是app </p>
    <router-view > </router-view>           // 没有指定name属性,渲染名字为default的组件 
    <router-view name="a"> </router-view>   // 根据name 属性,查看当前被匹配的所有的组件中是否有 name为 "a"的组件,有则渲染
    <router-view name="b"> </router-view>      
</div>

<script>
var router = new VueRouter({     // 一个路由对象,内部配置 路径映射关系
  routes:[
    { path: '/foo', components: {   // 匹配到多个组件
        default: comp1,    //  key为组件在 路由中的名字,会对应 router-view 中的name属性进行匹配
        a: comp2,
        b: comp3,
      }
    }, 
    { path: '/bar', components: {} },
    ]
})

var vm = new Vue({
  el:"#app",
  data:{},
  methods:{},
  router: router,
})
</script>

嵌套路由

/user/profile                         /user/posts
+------------------+                  +-----------------+
| User             |                  | User            |
| +--------------+ |                  | +-------------+ |
| | Profile      | |  +------------>  | | Posts       | |
| |              | |                  | |             | |
| +--------------+ |                  | +-------------+ |
+------------------+                  +-----------------+

官方的展示很形象,当访问/user/ 前缀时,会渲染这个user 的组件,而在user组件内部,有一个小的组件,这个组件的内容由 /user/之后的路劲进行匹配,渲染匹配到的子组件。vue 使用了嵌套路由的方式实现这样的功能。

<div id="app">
  <router-view></router-view>  // 外层用于放置 user 这一层组件的位置。内层组件的坑显然应该在外层组价(也就是user组件)内部定义
</div>


<script>
  var comp = Vue.component("comp", {
      template:"<div>  <router-view> </router-view>  <div>"   //  comp 组件内部 渲染路由组件。
  })
  
  var router = new VueRouter({     // 一个路由对象,内部配置 路径映射关系
    routes:[
      { path: '/user', component:comp,   // 当匹配到/user,会将comp 渲染到 #app 中的router-view位置。
        children: [
        {
          path: 'profile',       // 继续匹配到profile,也就是/user/profile时,会将UserProfile 渲染到 comp 中的router-view
          component: UserProfile
        },
        {
          path: 'posts', 
          component: UserPosts
        },
      }, 
        ]
    })
  
  var vm = Vue({
  var vm = new Vue({
    el:"#app",
    data:{},
    methods:{},
    router: router,  
    })
  })
</script>

通过指定 children 属性的方式,可以再定义一层路由的匹配规则,这些规则匹配到的组件会再其父组件的router-view中渲染,路由的嵌套,也是组件的嵌套。

children 属性的配置和 routes 配置结构是相同的,可以在 children 中再定义children,实现多层的嵌套,同时children中的每一个路由项目,也是可以映射多个组件,并使用命名组件的。

注意:子路由规则前面不要使用"/",使用后将会按照从url的根开始匹配,而不是从父路由匹配结束位置继续匹配。

定义路由的规则及参数

动态匹配参数

上面的路由是使用字符串的形式完全匹配的,但是有时候url中的路径是一些变化的值, 例如用户/user/id这种url

/user/1
/user/2

上面的url都应该指向同一个组件对象,当时组件对象又应该获取他们不同id值。vue提供了这种动态路由匹配的方式。我们如此定义路由即可

var router = new VueRouter({     // 一个路由对象,内部配置 路径映射关系
    routes:[
      { path: '/user/:id ', component:comp},  //  匹配 /user/ 前缀的,后面部分的值由id接收
        ]
    })

:id 会进行动态的匹配任意值,接收的值会放入router对象的params属性中,属性值为一个对象。当我们访问 /user/1 这个url时候,将会展示comp这个组件,并且在这个组件的内部,我们可以查看其router.params属性,可以得到一个 {"id":"1"}这样的对象,id的值会随着匹配到的url中的值改变,这样就实现动态路由。:id 这样的匹配方式会以 / 为边际,不会匹配超过 / 边界的内容。例如 /user/1/profile 的 id 匹配到 1 而不会是 1/profile。如果要匹配多个可以使用多个这样的动态参数,路由规则可可以定义为 /user/:id/:foo ,这样匹配 /user/1/profile的结果则是, $route.params = { id:"1", foo:"profile"} 

路由参数

url 中可以使用查询字符串的方式,查询字符串不会影响 路由的匹配,只是在匹配后向组件添加了一些参数(/user/?id=1&name=tom 和 /user/ 在匹配时的表现是相同的),这些参数可以在组件this.$route.query对象中查看。

例如访问的url的hash为: /user/?id=1&name=tom

var router = new VueRouter({    
    routes:[
      { path: '/user', component:comp},  // 匹配规则
        ]
    })

通过上面的url访问,我们可以在路由的query中得到这样的值, this.$route.query => { "id": "1", name: "tom" } ,无论是使用动态路由,访问$route.params 对象还是使用 查询字符串,访问$route.query 对象,都可以实现url 对组件内部传值的的功能。

匹配规则中还可以使用通配符来匹配路径

{ path: '*'}         // 会匹配所有路径
{ path: '/user-*'}   // 会匹配以 `/user-` 开头的任意路径

这样的匹配规则通常用来捕捉url的错误,当上面所有路由都没有匹配到时,将会匹配到该项。

重定向和别名

// 重定向,访问'/a' 时,将用户访问重新定位到访问 '/b' 上,有一个重定向的过程
const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }                      // url匹配到/a 路径,会重定向到 /b
    { path: '/c', redirect: from => { return '/b'}  }   // from 参数为'/c', return 值为重定向的路径。
    { path: '/b', redirect: comp}
  ]
})

// 别名:访问 "/b" 只是/a 的一个别名,两个规则都指向同一个组件,所以访问两个url 都想过都相同,不会发生重定向。
const router = new VueRouter({
  routes: [
    { path: '/a', component: A, alias: '/b' }
  ]
})

路由导航标签 router-link

定义好了路由对象和对应的匹配规则,我们还需要能快速跳转到这些 url 的方式,vue使用了 router-link 标签来 实现导航链接,这些导航链接标签会被默认渲染为 一个 <a> 标签。可以通过 该标签的 tag 属性指定 渲染的标签名。当然也可以使用 <a> 标签,注意使用时 href 属性的指定的路径应该是hash值中的路径,也就是 <a href="#/user/profile"> </a> ,而不是直接跳转 url 中的路径,所以 应该带上 # 。

使用 router-link 的原因主要是更加的方便和灵活,例如可以向<a>标签那样指定跳转路径

<router-link to="/user" tag="button"> 用户模块 </router-link>     //   to 指定跳转的路径, tag指定渲染为一个button按钮
<router-link to="/user/123" tag="button"> 用户模块 </router-link>
<router-link to="/user/?id=123" tag="button"> 用户模块 </router-link>

可以使用命名路由的名字,并携带参数。使用这种方式需要我们为每一个路由项添加一个name属性

const router = new VueRouter({
  routes: [
    {
      path: '/user/:userId',
      name: 'a',             //  该路由项的name 为 a,所以跳转时候,只需要指定该名即可指向这条路由
      component: User
    }
  ]
})

<router-link :to="{ name: 'a', params: { userId: 123 }}"  tag="button"> 用户模块 </router-link>
// 指定跳转到 路由项目为 name 为a 的这条路由上,并通过params: { userId: 123 }注入参数,还可以使用query:{"id":123} 注入数据
// to 指定的一个对象,这里需要对 to 进行属性绑定,也就是 v-bind, to 的值才不会被解析为一个字符串。

代码中实现url的跳转

代码中实现url跳转的方式就是手动的在 Vue对象的history对象中 push 或者 pop url页面信息,当history 栈顶发生变化,页面也会对应变为栈顶对应的页面。

当我们点击 router-link 标签时,当前url将会改变为这个标签中to属性指向的路径,从而页面发生改变或者刷新,而点击标签能够发生跳转的动作,实际上是通过点击事件向 一个 history 栈 中添加了这个新的url 记录实现的。这里的history栈 是当页面加载后,vue中生成的一个记录所有页面跳转的栈的数据结构,这个栈会从一开始记录所有的url访问记录,且栈中最新的元素也就当前页面的信息,当我们需要展示新的页面时,将最新的内容push 到这个 history 中,由于栈的最新元素发生了改变,所以页面跳转了,同理,如果点击浏览器的后退按钮,将会从这个history 栈中 pop 最新的元素,所以页面 会回到上一个操作的页面。

所以如果我们想要在代码中实现页面的跳转,push 最新的页面内容即可,vue 为我们在做好了封装而方便我们去操作这个histroy从而在代码层面实现页面的跳转,push 的对象 同router-link中使用的对象即可。例如

// 有这样一个路由器,内部只定义了一条路由规则
const router = new VueRouter({
  routes: [
    {
      path: '/user/:userId',
      name: 'a',  
      component: User
    }
  ]
})

var vm = new Vue({
  el:"#app",
  data: {},
  router:router,
  methods:{
      optHistory(){
      // 调用router中的push方法,将新的元素也就是router-link 中的 to 属性的值,添加到 history 的最新页面中即可。
      // router 内部会解析并找到对应的路由信息。
      this.$router.push('user/123')
      this.$router.push({path:"'/user/123'"})
      this.$router.push({ name:"a", params: { userId: 123 }})  // 向history 中添加新的路由信息,跳转到新的页面
    }
  }
})

// 这是一个点击调转的按钮,如果点击,将会根据 路由项 的name,匹配到上面那一条路由,页面发生跳转。
<router-link :to="{ name: 'a', params: { userId: 123 }}"  tag="button"> 用户模块 </router-link>

// 我们已经知道了页面跳转实际上是history栈中添加了新的元素,而history 栈由router内部,所以,我们可以调用上面method 中的
// optHistory方法,也可以实现点击跳转按钮的功能。

除了push 方法,还可以调用replace(),将最新的记录替换,而不是进行添加。同样的我们也可以访问栈中原来的元素实现页面回退

router.go(-1)   // 回退一个记录
 
router.go(1)    // 前进一个记录
router.go(-3)   // 回退三个记录

总结路由的使用

这是路由器的基本使用形式,主要包括几个步骤

  • 创建路由器并在内部建立路径和组件的映射关系, 嵌套路由使用children指定,匹配路径时可以使用 ":username" 的动态匹配方式。
  • 路由对象创建完成后,将这个路由器挂载到 vue 对象上,vue对象即可使用。
  • 在这个view对象管理的区域使用 <router-view> 标签指定一个渲染组件的位置,这个vue对象会将根据当前路径匹配到的组件渲染到这个位置,如果时嵌套路由,子路由匹配的组件应该在父路由对应的组件中定义。
  • 页面中使用 router-link 标签实现url 的改变,类似于<a>标签,但使用更灵活

每次url 发生改变,路由器就会匹配当前路径,得到一个组件进行渲染。

原文地址:https://www.cnblogs.com/k5210202/p/13850087.html