Vue.js 笔记09

发布于 2020-06-02  254 次阅读


关键词:vue-router

Vue.js提供了插件机制,可以在全局添加一些功能。它们可以简单到几个方法、属性,也可以很复杂,比如一整套组件库。

前端路由

SPA(单页面富应用)的核心是前端路由,路由,通俗来讲就是网址。
在专业一点,就是每次GET或者POST等请求在服务端有一个专门的正则配置列表,然后匹配到具体的一条路径后,分到不同的Controller,进行各种操作,最终将html或数据返回给前端,这就完成了一次IO,这种方式被称为后端路由,也就是多页面的。

后来,有了前后端分离的开发模式,后端只提供API来返回数据,前端通过Ajax获取数据后,再用一定的方式渲染到页面里。所以,SPA就是在前后端分离的基础上,加一层前端路由。

前端路由,即由前端来维护一个路由规则。实现有两种,一种是利用url的hash,就是常说的锚点(#),JavaScript 通过hashChange事件来监听url的改变;另一种就是HTML5的 History 模式,它使url看起来像普通网站那样,以"/"分割,没有"#",页面也不会跳转,这种方法需要服务端支持,服务端在接收到所有的请求后,都指向同一个HTML文件,否则出现404,因此,SPA只有一个html,整个网站所有的内容都在这一个html里,通过js来处理。

vue-router基本用法

(不以webpack打包的项目结构演示,直接在页面demo中演示)

本地环境下获取vue-router.js文件,然后通过<script>标签引入

<script src="./js/vue-router.js"></script>

引入后,写一个简单模板演示vue-router的代码:

<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script src="./js/vue.js"></script>
		<script src="./js/vue-router.js"></script>
	</head>
	<body>
		<div id="app">
			<div>Header</div>
			<main><router-view /></main>
			<div>Footer</div>
		</div>
		<template id="pagehome">
			<p>This is HomePage</p>
		</template>
		<template id="pageabout">
			<p>This is AboutPage</p>
		</template>
		<script>
			const pageHome = {
				template:pagehome
			}
			const pageAbout = {
				template:pageabout
			}
			const router = new VueRouter({
				routes:[
					{path:'/',component:pageHome},
					{path:'/about',component:pageAbout}
				]
			})
			var vm = new Vue({
				el:'#app',
				router
			})
		</script>
	</body>
</html>

根据url的路径,页面会自动切换<router-view/>处的视图,而不会刷新跳转网页

vue-router 默认使用URL hash 来存储路径,在这种方式下,为了访问例如
example.com上的/about路由,就需要跳转到example.com/#about
现在很多浏览器都支持HTML5 history API,这使得开发者无需跳转到一个新的页面就能更新页面的URL,只需要将路由的mode改为history:

const router = new VueRouter({
				mode:'history',
				routes:[
					{path:'/home',component:pageHome},
					{path:'/about',component:pageAbout}
				]
			})

链接导航

使用history模式时,页面的切换需要依靠<router-link>标签来实现,例如

<router-link to="/home">go to home</router-link>
<router-link to="/about">go to about</router-link>

这样在不同内容钟切换时,页面是不需要重新加载的,大幅提高了网站速度,因为所有的HTML、JavaScript和CSS都已经被下载下来了
<router-link>标签在用于hash模式时,同样能顺利切换,它能自动打理路由模式

<router-link>除了to以外还有几个常用的属性:

tag属性

默认情况下,使用<router-link>标签会在页面上渲染处<a>标签,例如上面的两行代码渲染结果就是:

<a herf="/home">go to home</a>
<a herf="/about">go to about</a>

在一些时候还需要渲染除了锚点以外的元素,比如导航栏的列表元素,这时就可以通过tag属性来做到:

<router-link to="/home" tag="li"> go to home</router-link>

上述代码中的渲染结果为:

<li>go to home</li>

之后Vue会往这个元素中添加一个事件监听器,以监测它被单击的时机,从而处理导航,但因此我们失去了锚点和href属性,浏览器不能把它原生识别为一个链接,造成一些我们不想要看到的效果,因此,我们可以在<router-link>元素里加上<a>锚点标签:

<router-link to="/home" tag="li"><a>go to home</a></router-link>

这样渲染出来的结果就是:

<li><a href="/home">go to home</a></li>

active-class属性

当<router-link>组件的 to 属性中的路径与当前页面的路径相匹配时,链接就被激活了( active )。假如处在/home,那么该链接就是激活的。

当链接被激活时,vue-router 会自动为生成的元素赋一个类(class)。在默认情况下,这个类是router-link-active ,不过你可以通过active-class属性来配置这个类。比如,你正在使用 Bootstarp 制作一个导航栏(navbar),就可以设置当前被激活的链接类名为 active:

<ul class="nav navbar-nav">
        <router-link to="/home" active-class="active" tag="li"><a>Home</a></router-link>
        <router-link to="/about" active-class="active" tag="li"><a>About</a></router-link>
</ul>

原生事件

给某个<router-link>添加一个事件处理器( event hanlder) 时,可以用@click再加上.native修饰符来监听:

<router-link to="/home" @click.native="handleClick">Home</router-link>

单击这个链接时,handleClick就会被调用

编程式导航

除了用链接的方式来为用户提供导航功能,还可以运用路由器的一些实例方法来实现编程式导航。这些方法效仿浏览器原生的 history 方法,如history.pushState( )、history.replaceState( )以及history.go( )

可以用router.push(或组件实例中的 this.$router.push() )来进行路径跳转

router.push("/about")

这种方式与单击一个to属性为 /about 的<router-link>组件完全等效, 事实上,单击<router-link>时,组件内部用的就是router.push( )方法

还有router.replace( )方法,它与router.push表现类似,都是导航至指定的路由,不同点在于,router.push会向history栈里添加一个新的记录,因此,如果用户按下返回键,路由器会跳到上一个路由,而replace则替换了当前的history记录,所以返回键只会回到之前的路由

对于router.go( )方法,它能让你在历史记录中前进和后退,后退一条记录,就用

router.go(-1)

前进十条记录,则用

router.go(10)

如果历史中没那么多记录,那么到达最前或最后时,调用就会自动终止。

导航守卫

假设想要限制未登陆的用户,禁止这些用户访问你应用的某些部分,同时你还有一个userAuthenticated( )方法用于当用户登录时返回 true。
基于上面的要求,可以为路由器添加一个router.beforeEach( )守卫,该守卫被传入3个参数:to、from及next,其中from和to分别表示导航从哪里来和到哪里去,而next则是一个回调,在里面可以让vue-router去处理导航、取消导航、重定向到其他地方或注册一个错误。

下述是一个例子,它以全局注册的方式阻止未认证用户访问 /account 路径下的任何内容:

    router.beforeEach(function(to,form,next){
        function userAuthenticated(){
            return false
        }
        if(to.path.startsWith('/account')&&!userAuthenticated())
            next('/login')
        else
            next()
    })

但,在守卫中一个一个去检查路径会让程序变得冗长难懂,路由元信息可以很好地解决这个问题。可以在路由上添加一个 meta 属性,并在守卫中重新获取它:

const router = new VueRouter({
	mode:'history',
	routes:[
		{
		path:'/account',
		component:pageAccount,
		meta:{
	           requiresAuth:true
                   }
		},
		{path:'/login',component:pageLogin}
	]
})
router.beforeEach(function(to,form,next){
        function userAuthenticated(){
            return false
        }
	if(to.meta.requiresAuth&&!userAuthenticated())
		next('/login')
	else
		next()
})

现在,无论用户在任何时候访问 /account ,路由器都会检查其 requiresAuth 属性,如果用户尚未认证,则会被切换到登录页面。
当使用嵌套路由时,to.meta 指向的是子路由的元信息,而非其父路由,也就是说,如果在 /account 上添加了meta属性,但用户访问的是 /account/id ,则to.meta获取到的对象是关于子路由的,而不是父路由。可以通过to.matched的方法来曲线救国

router.beforeEach(function(to,form,next){
	function userAuthenticated(){
		return false
	}
	const requiresAuth = to.matched.some(function(record){
		return record.meta.requiresAuth;
	})
	if(requiresAuth&&!userAuthenticated())
		next('/login')
	else
		next()
})

现在,如果访问 /account/id ,而该路由又是 /account 的子路由,那么它们两个的meta对象都会被检查,而不只是 /account/id 的meta

除了 beforeEach 这个运行在导航之前的守卫,还有一个运行在导航之后的守卫 afterEach ,这个守卫只被传入两个参数 to 和 from,因此不会影响导航,可以用来做一些其他操作,比如调用to.meta.title来设置跳转后的标题,具体用法和 beforeEach 大同小异

vue-router在内部通过遍历路由数组的方式挑选被显示的路由,并选取其中匹配到当前URL的第一个:

const router = new VueRouter({
	mode:'history',
	routes:[
		{
			path:'/home/:userID',
			component:pageUser',
		},
		{
			path:'/home/me',
			component:pageMe',
		}
	]
})

在上述路由中,我们是无法通过 /home/me 访问到pageMe页面的,因为路径会首先被 /home/:userID 匹配(动态路由的内容在下面一小节中) ,所以 userID就被设置成了 me,从而显示的是pageUser组件,如果需要访问 pageMe组件,就需要把该路径对象写在动态路由的前面防止误匹配

动态路由

如果想要匹配用户的ID作为路径的一部分并利用它,可以使用动态路径匹配,也就是说可以通过一种专门的语法来指定路径规则,假如我们想要让组件在路径是 /user/1234 时被显示,而其中的1234可以改为任意内容,就可以像下面这样指定路由:

const router = new VueRouter({
	mode:'history',
	routes:[
		{
			path:'/user/:userID',
			component:pageUser',
		}
	]
})

这样,任何匹配到 /user/:userID 的路径都会被渲染出 pageUser 组件

在组件实例中,可以通过使用属性 this.$route 来获取当前的路由对象,这个对象包括了一些有用的属性,诸如当前被访问的完整路径、URL的查询参数等,就上面这个例子来说,最有用的属性就是 params,它包含了被动态匹配的URL的各个部分。如果访问/user/1234,那么params就等于:

{
	"userID":"1234"
}

动态匹配规则不需要一定在URL的末尾,/user/1111/info 路径下的 1111 也仍然能够被匹配到,同样地,也可以设置多段动态部分,例如 /user/:userID/post/:pageNum,能同时匹配多段数据,并在params中生成对应个数的可用数据

路由参数作为组件属性传入

除了在组件中使用 this.$route.params ,还可以让 vue-router 将 params 作为路由组件的 props 传入,如:

<template id="pageuser">
	<p>User ID: {{$route.params.userID}}</p>
</template>
<script>
	const pageUser = {
		template:pageuser
	}
	const router = new VueRouter({
		routes:[
			{
				path:'/user/:userID',
				component:pageUser
			}
		]
	})
</script>

当导航至 /user/1234时,{{$route.params.userID}}就会被自动替换为1234

如果想要vue-router 将 userID 作为组件的一个属性传入,可用在路由中指定 props:

<template id="pageuser">
	<p>User ID: {{userID}}</p>
</template>
<script>
	const pageUser = {
		props:['userID'],
		template:pageuser
	}
	const router = new VueRouter({
		routes:[
			{
				path:'/user/:userID',
				component:pageUser,
				props:true
			}
		]
	})
</script>

使用 $route.params来代替props的好处是,组件与 vue-router 不再紧密耦合。

嵌套路由

在某个路由区域内如果还存在其他使用频率高的子路由,它只有一小块内容会变化,这种情况下可用使用嵌套路由。

嵌套路由允许指定子路由,并用另一个<router-view />来显示其内容,如:

const router = new VueRouter({
	routes:[
		{
			path:'/setting',
			component:pageSettings,
			children:[
				{
					path:'profile',
					component:pageSettingProfile
				},
				{
					path:'email',
					component:pageSettingEmail
				}
			]
		}
	]
})

只需要在 pageSetting 组件中加入一个<router-view />即可

重定向和别名

vue-router可使用一个 redirect 属性,用于代替component 实现重定向:

const router = new VueRouter({
	routes:[
		{
			path:'/setting',
			redirect:'/preferences'
		}
	]
})

另一种重定向的方法,就是给组件取一个别名,例如,如果你想让一个页面从 /settings 和 /preferences 都可以被访问,则可以给 /settings 路由取一个叫 /preferences 的别名:

const router = new VueRouter({
	routes:[
		{
			path:'/setting',
			alias:'/preferences',
			component:pageSettings
		}
	]
})


何为BS?B-big,S-shui。