Vue学习笔记-路由专题

5 篇文章 0 订阅
2 篇文章 0 订阅

上篇回顾:Vue基础

概述

上一期介绍了Vue的基本功能,这期着重说一下Vue的重点之一:路由
Vue的路由,将以往我们原生界面的跳转整理在了一起,供我们可以集合起来进行调配。
而且,配合路由,我们可以很容易地将我们的应用变成单页应用。
再添加上一些页面切换效果,甚至可以作为H5嵌套到APP里面模拟出原生应用的效果。

1、起步

要使用路由,在引入vue.js的基础上,还要引入vue-router.js
下面先来看一个示例:
(界面部分)

	<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" tag=“span”>Go to Foo</router-link>
		<router-link to="/bar">Go to Bar</router-link>
	  </p>
	  <!-- 路由出口 -->
	  <!-- 路由匹配到的组件将渲染在这里 -->
	  <router-view></router-view>
	</div>

如上,界面的路由主要由两部分组成:

  1. <router-link>:导航(类似跳转按钮),设置跳转路径。
    默认显示为<a>标签,也可以通过tag=“span”进行指定。
    to="[路径]"指定链接的地址。
  2. <router-view>:跳转界面的展示区域。
    用于加载to="[路径]"链接界面的内容

(js部分)

	// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)
	// 1. 定义(路由)组件。
	// 可以从其他文件 import 进来
	var Foo = { template: '<div>foo</div>' }
	var Bar = { template: '<div>bar</div>' }

	// 2. 定义路由
	// 每个路由应该映射一个组件。 其中"component" 可以是
	// 通过 Vue.extend() 创建的组件构造器,
	// 或者,只是一个组件配置对象。
	// 我们晚点再讨论嵌套路由。
	var routes = [
	  { path: '/foo', component: Foo },
	  { path: '/bar', component: Bar }
	]

	// 3. 创建 router 实例,然后传 `routes` 配置
	// 你还可以传别的配置参数, 不过先这么简单着吧。
	var router = new VueRouter({
	  routes // (缩写)相当于 routes: routes
	})

	// 4. 创建和挂载根实例。
	// 记得要通过 router 配置参数注入路由,
	// 从而让整个应用都有路由功能
	var app = new Vue({
	  router
	}).$mount('#app')

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

以上,为一个简单的路由js配置,分为四个步骤:

  1. 声明路由组件,定义需要跳转的界面,如:
    var Foo = { template: '<div>foo</div>' }
  2. 定义路由配置,将 路由路径路由组件 匹配起来,如:
    var routes = [ { path: '/foo', component: Foo } ]
  3. 创建路由的 router 实例,进行一些基本的配置,如:
    var router = new VueRouter({ routes: routes })
  4. new Vue() 并加入路由配置,如:
    new Vue({ router }).$mount('#app')

2、动态路径参数

Vue路由地址有几种携带参数的方式:

  • /后用来声明,后面拼接上参数名,如:{ path: '/user/:idtest', component: User } 通过 {{ $route.params.idtest }} 在跳转后的内容中获得参数值。 在写实际链接地址的时候,忽略:,直接写入参数值,如: <router-link to="/user/foo">/user/foo</router-link> <router-link to="/user/bar">/user/bar</router-link> 以上会进入同一个页面,而
    foo、bar就是各自携带的参数,会被设置到 this.$route.params,可以通过 {{ $route.params.idtest }} 获得。
  • 传递多个参数,也可以这样:/user/:username/post/:post_id
  • 其他参数,如果带查询参数,可以这样:
	// 带查询参数,变成 /user?plan=private
   	{ path: '/user', component: User, query: {plan: 'private' }})  	
  • 动态参数还可以使用 命名路由 的形式来赋值:
    声明时,name指路由名称。
    在界面上调用时,直接name引用名称,params记录参数即可。
    示例:
   { path: '/user/:idtest', name:'test', component: User } 			
   <router-link :to="{name:'test', params:{idtest: '11'}}">/user/foo</router-link>
  • 监控路由跳转
		watch: {
  				'$route' (to, from) {
  					//to:目的地
  					//from:当前地址
  				} 			
  			} 	
  • 同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。

3、嵌套路由

即在原来的路由配置下,加上children属性,children的配置也一样是原来的路由配置方式。
而对应有children的路由页面,也加入<router-view>以供加载子路由。
:子路由继承父路径,所以子路由的path只需设置后面的内容,且不用/开头。
示例

	var User = {
	  template: 
	    `<div class="user">
		  <h2>User {{ $route.params.id }}</h2>
		  <router-view></router-view>
		</div>`
	}

	var router = new VueRouter({
	  routes: [
		{ path: '/user/:id', component: User,
		  children: [
			{
			  // 当 /user/:id/profile 匹配成功,
			  // UserProfile 会被渲染在 User 的 <router-view> 中
			  path: 'profile',
			  component: UserProfile
			},
			{
			  // 当 /user/:id/posts 匹配成功
			  // UserPosts 会被渲染在 User 的 <router-view> 中
			  path: 'posts',
			  component: UserPosts
			}
		  ]
		}
	  ]
	})

4、编程式的导航

路由导航跳转的原理:点击 <router-link>,内部会调用router.push(...)
所以,除了在页面声明<router-link>,也可以在js使用router.push(location)来进行跳转页面。
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。
如:

  • 字符串:router.push('home')
  • 对象:router.push({ path: 'home' })
  • 命名的路由:router.push({ name: 'user', params: { userId: 123 }})
  • 带查询参数:router.push({ path: 'register', query: { plan: 'private' }})

arouter.push 方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
brouter.replace(location)router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
声明方式 <router-link :to="..." replace>
crouter.go(n) 参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)。(正数前进,负数返回)

5、命名的路由

在路由的设置里面,加入字段name,作为路由的名称。
界面上就可以用路由的名称来调用路由了。

	var router = new VueRouter({
	  routes: [
		{
		  path: '/user/:userId',
		  name: 'user',
		  component: User
		}
	  ]
	})

要链接到一个命名路由,可以给 <router-link>to 属性传一个对象:
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
这跟代码调用 router.push() 是一回事:
router.push({ name: 'user', params: { userId: 123 }})
这两种方式都会把路由导航到 /user/123 路径。
: 是params , 不是param

6、命名视图

对于想要同时(同级)展示多个视图的,就需要为每个视图命名,以进行区分。
router-view 没有设置名字时,默认为 default
当需要设置名字时,直接加属性name即可,如下:
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。
在js上配置多个视图,使用components进行配置(区别于单个视图的component,多个需要加s)。示例如下:

	var router = new VueRouter({
	  routes: [{
		  path: '/',
		  components: {
			default: Foo,
			a: Bar,
			b: Baz
		  }
		}]
	});

7、重定向和别名

  • 重定向:当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,如:
 { path: '/a', redirect: '/b' } 			
 { path: '/a', redirect: { name: 'foo' }} 			
 { path: '/a', redirect: to => {
   			  // 方法接收 目标路由 作为参数
   			  // return 重定向的 字符串路径/路径对象 			
  }}
  • 别名/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样,如:
    { path: '/a', component: A, alias: '/b' }

8、导航钩子

主要用来拦截导航,让它完成跳转或取消。可以在跳转前、后进行一些自定义操作。

  • 全局beforeEach钩子
    在导航跳转开始之前调用,按这些钩子创建的顺序进行调用。
    调用完根据最终返回结果,确定导航是否进行跳转。
    在执行钩子的过程中,导航会一直处于等待中。

    	var router = new VueRouter({ ... })
    	router.beforeEach((to, from, next) => {
    	 	// ... 
    	})
    

    参数说明
    to: 即将要进入的目标 路由对象
    from: 当前导航正要离开的路由
    next: 是一个function,一定要调用该方法来最终决定这个钩子的执行结果。可选的内容如下:

    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
    • next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
    • next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
      你可以向 next() 传递任意位置对象,且允许设置诸如 replace: truename: 'home' 之类的选项以及任何用在 <router-link>to proprouter.push() 中的选项。
    • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
      :确保要调用 next 方法,否则钩子就不会被 resolved。
  • 全局解析钩子beforeResolve (2.5.0+ 版本新增)
    router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内钩子和异步路由组件被解析之后,它才会被调用。

  • 全局的after钩子
    在导航完成后调用。与before钩子类似,但没有 next 方法,因为是在导航完成后执行,所以不能改变导航:
    router.afterEach((to, from)=> { // ... })

  • 某个路由独享的钩子
    你可以在路由配置上直接定义 beforeEnter 钩子:

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

:这些钩子与全局 before 钩子的方法参数是一样的。

  • 组件内的钩子
    直接以例子说明:
	var 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回调方法,所以可以在next的回调函数里调用this
完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用离开钩子。
  3. 调用全局的 beforeEach 钩子。
  4. 在重用的组件里调用 beforeRouteUpdate 钩子 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 钩子 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 用创建好的实例调用 beforeRouteEnter 钩子中传给 next 的回调函数。

9、router-link

  • <router-link>的两种写法:
    • 直接to,后面带跳转链接;
    • 绑定的to,后面链接(或者别名)需要特别加上'', 如:v-bind:to="'home'":to="'home'"
	<!-- 写法示例 -->	
	<!-- 1、字符串 -->
	<router-link to="home">Home</router-link>
	<!-- 渲染结果 -->
	<a href="home">Home</a>

	<!-- 2、使用 v-bind 的 JS 表达式 -->
	<router-link v-bind:to="'home'">Home</router-link>
	<!-- 3、不写 v-bind 也可以,就像绑定别的属性一样 -->
	<router-link :to="'home'">Home</router-link>
	<!-- 4、同上 -->
	<router-link :to="{ path: 'home' }">Home</router-link>
  • 设置 append 属性后,则在当前(相对)路径前添加基路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了append,则为 /a/b,例如:
    我们的当前路径为/link
    <router-link :to="{ path: 'relative/path'}"></router-link>:指向路径为/relative/path
    <router-link :to="{ path: 'relative/path'}" append></router-link>:指向路径为/link/relative/path
  • 有时候想要 <router-link> 渲染成某种标签,例如 <li>。 可以使用 tag 进行指定,如:tag="li"
  • active-class: 设置 链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置

10、路由元信息meta

在配置路由的时候,直接加入字段meta即可进行配置:
{ path: 'bar', component: Bar, meta: { requiresAuth: true } }
使用的时候,遍历$route.matched来检查路由记录中的 meta 字段。
为什么是遍历?
首先,我们称呼 routes 配置中的每个路由对象为路由记录
路由记录可以是嵌套的,因此,当一个路由匹配成功后,他可能匹配多个路由记录。例如,根据上面的路由配置,/foo/bar 这个 URL 将会匹配父路由记录/foo以及子路由记录/bar两个路由记录
一个路由匹配到的所有路由记录会暴露为$route对象的 $route.matched数组,所以我们需要遍历 $route.matched 来检查路由记录中的 meta 字段

11、界面特效

vue还提供了路由过渡的界面效果,和页面滚动效果。配合这些,可以让前端设计得更人性化。在移动端单页面的应用下,能更加贴近App的使用效果。

其他补充

  1. data参数监控
    vm.$watch(expOrFn, callBack, [options])
    参数
    expOrFn --> String 或 Function
    callBack --> 回调函数
    options --> 对象
    options取值:
    deep:boolean型。深度监视,为了发现对象内部值的变化,可以在选项参数中指定 deep: true 。注意监听数组的变动不需要这么做。
    immediate:boolean型。在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调。
    用法:观察 Vue 实例变化的一个表达式或计算属性函数,回调函数得到的参数为新值和旧值,表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。
		var vm = new Vue({
			el:'#app',
			data:{
				a:123,
				b:321
			}
		});
		
		//监听数据变化
		vm.$watch('a',function(){
			alert('数据a 和 数据b 发生变化了');
			this.b = this.a + 100;
		},{deep:true});
  1. 限定样式仅限当前组件使用,使用scoped:在style中写入scoped,如: <style scoped></style>

  2. $nextTick:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
    即:在某控件发生变动后执行的方法。
    下面引用一段话说明什么时候需要用到Vue.nextTick()

你在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。原因是什么呢,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。
在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。
原因是,Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
当你设置 vm.someData = ‘new value’,DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。

为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
示例:

	watch:{
		data: function(){
			this.$nextTick(function(){
				//TODO:
			});
		}
	}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值