一、前端路由
前端路由是指由前端控制URL到页面的映射关系,即改变URL时,页面不进行刷新。
改变URL通常有以下两种方式:
1.1 URL的hash模式
URL的hash是指锚点(#),本质是改变window.location的href属性,可以直接通过location.hash
改变href属性,但页面不发生刷新。
1.2 HTML5的history模式
1.2.1 pushState
也可以通过history.pushState
改变URL,页面也不会发生刷新。
类似一个栈结构,通过pushState入栈,back出站:
1.2.2 replaceState
在pushState方法下,可以使用浏览器的回退按钮,如果使用history.replaceState
改变URL,是不可以使用回退按钮的,此时直接替换了URL。
1.2.3 go
-
history.back()
等价于history.go(-1)
-
history.forward()
等价于history.go(1)
-
相当于浏览器界面的前进和后退
二、vue-router
2.1 安装和使用
2.1.1 安装vue-router
npm install vue-router --save-dev
或者直接使用脚手架创建项目时,勾选vue-router
2.1.2 搭建router配置框架
在根目录下src
中会多出一个router
文件夹,其中包含一个index.js
文件,在这个文件里用于定义前端路由,使用脚手架会自动生成(去掉了helloworld组件相关内容):
import Vue from 'vue'
import Router from 'vue-router'
// 通过vue.use(插件)安装路由插件
Vue.use(Router)
// 创建一个Router对象实例,并在routes中配置路由和组件的映射关系
// 再将对象导出,以便在vue实例中导入
export default new Router({
routes: [
]
})
配置好后,在main.js
中,导入router,在vue实例中挂载即可。
2.1.3 使用vue-router
- 首先在
src
的components
文件夹中创建两个组件home.vue
与blog.vue
。
- 在
src
–>router
文件夹中的index.js
里导入这两个组件,并且在Router实例中配置对应的路径(跟在URL后)与组件名称,进行路由的映射:
import Vue from 'vue'
import Router from 'vue-router'
import Home from '../components/home'
import Blog from '../components/blog'
Vue.use(Router)
export default new Router({
routes: [
{
path:'/home',
component:Home
},
{
path:'/blog',
component:Blog
}
]
})
- 页面会显示
App.vue
中的内容,所以在其中通过<router-link>
与<router-view>
使用路由:
<template>
<div id="app">
<router-link to='/home'>首页</router-link>
<router-link to='/blog'>博客</router-link>
<router-view></router-view>
</div>
</template>
<router-link>
: vue-router中内置的组件,会被渲染成一个<a>
标签;<router-view>
: 根据当前路径,在页面上动态渲染出不同的组件。
- 配置默认路径
默认路径是指进入网站时首先渲染的页面内容,在这里如果想显示home组件的内容,只需要在路由中添加根路径’/'的映射重定向到/home路径下即可:
routes: [
{
path:'/',
redirect:'/home'
}]
- 去掉URL中添加的/#
改变URL时,路径中间会加入一个#号,是因为此时默认使用的是hash模式。要去除#号,在Router实例中将hash模式改变为history模式即可。
//src-router-index.js
export default new Router({
mode:'history',
routes:[...]
})
2.2 router-link属性
<router-link>
除了to
属性外,还有一些常用的属性:
2.2.1 tag
属性
将默认渲染的<a>
标签,替换为指定的标签样式。
//指定为按钮样式
<router-link to='/home' tag='button'>首页</router-link>
2.2.2 replace
属性
添加replace属性后,不可以使用浏览器的回退与前进按钮。
<router-link to='/home' tag='button' replace>首页</router-link>
2.2.3 active-class
属性
router-link对应的路由匹配成功后,当前元素会自动设置一个router-link-active
的class,此时可以通过这个class名称修改当前匹配路由的样式。
<style>
.router-link-active {
color:red;
}
</style>
- 如果觉得类名太长也可以在router-link标签上进行修改:
//修改为.active
<router-link to='/home' tag='button' replace active-class='active'>首页</router-link>
- 如果想全局修改类型,需要在Router实例中添加属性:
//src-router-index.js
export default new Router({
linkActiveClass:'active',
routes: [..]
})
2.3 使用方法跳转路由
在App.js中,也可以不使用router-link标签,通过方法来进行路由的跳转,这里首先直接定义两个button,绑定两个方法,在方法中使用内置的$router
进行跳转操作:
name:'App',
methods: {
homeclk(){
this.$router.push('/home');
},
blogclk(){
this.$router.push('/blog');
}
}
2.4 动态路由
如果需要在URL后跟的路径动态变化,就需要使用动态路由。比如在一个页面中,需要展示很多个产品,每个产品有对应的产品ID,通过URL+产品ID来实现动态跳转。
-
在
components
中添加一个prod.vue
组件,定义产品页面。 -
在
router-index.js
中添加路由信息,这里的路径通过:参数
拼接:
{
path:'/prod/:prodid',
component:Prod
}
- 在
App.vue
文件里的App实例-data属性中定义上面定义的参数prodid
的值,并且在模板标签中通过v-bind动态绑定值传给router-link的to参数:
<router-link :to="'/prod/'+ prodid" tag='button'>产品</router-link>
- 此时在页面中就激活路由时,URL后就会跟上data中传入的值:
- 在
prod.vue
页面可以在computed
属性中定义prodID()
,通过$route
获取当前alive的路由($router
是在index中创建的Router实例),再通过params.参数
将data传入的值取出来。
<template>
...
{{prodID}}
...
</template>
<script>
...
export default{
...
computed: {
prodID(){
return this.$route.params.prodid
}
}
...
}
</script>
2.5 打包文件
通过npm run build
打包生成的dist文件夹,通常包含以下内容:
其中:
.map
文件用于输出代码的错误信息css
文件夹将所有css样式整合到1个.css
文件中js
文件夹中,app
代表开发时写的代码,manifest
表示底层支撑代码(如包导入导出等),vendor
表示第三方支撑代码(vue等)
2.5.1 路由的懒加载
打包时不同的页面都会在同一个js文件中,往往会导致文件过大,当从服务器请求时,加载时间过长,用户浏览器长时间等待的情况。
而懒加载就是将路由对应的组件打包成一个个的js代码块,访问的时候才加载对应的组件。在路由中,直接引用组件,如下:
{
path:'/prod/:prodid',
component:() => import('../components/prod')
}
或者
const Prod = () => import('../components/prod')
再次打包,就会多出一个js文件:
懒加载的方式:
- (最古老的方式):Vue异步组件和webpack代码
const Prod = resolve => { require.ensure(['../components/Prod.vue'],() => { resolve(require('../components/Prod.vue')) })};
- AMD写法
const Prod = resolve => require(['../components/Prod.vue'], resolve);
- ES6写法
const Prod = () => import('../components/Prod.vue')
2.6 嵌套路由
有时会存在页面中再进行一次路由的情况,也就是组件中含有子组件,此时就需要嵌套路由。
- 首先在
components
文件夹里新建2个子组件,导出的名字分别为food
和pet
:
- 在路由
index.js
文件中给父页面添加children
属性,将子路由通过对象的方式传给父路由,并且同样可以设定默认显示的页面:
{
path:'/prod',
component: () => import('../components/prod'),
children: [
{
path: '/',
redirect: 'food'
},
{
path: 'food',
component: () => import('../components/ProdFood')
},
{
path: 'pet',
component: () => import('../components/ProdPet')
}
]
}
- 在父组件
prod.vue
中的<template>
里导入子组件的路由:
<router-link to='/prod/food'>食品</router-link>
<router-link to='/prod/pet' >宠物</router-link>
<router-view></router-view>
- 显示效果:
2.7 参数传递
在URL中可以传递参数,有以下两种方法:
2.7.1 params类型
- 通过冒号后跟参数配置路由格式
path:'/user/:userid'
- 在路径后跟上对应的参数值:
<router-link :to="'/user/'+ userid" tag='button'>用户</router-link>
2.7.2 query类型
当需要传入数据较多时,使用此方法。
- 在
<router-link>
中直接传入:
<router-link :to = "{path:'/mine',query:{name:'wht',age:18}}" tag="button">我的</router-link>
- 或者使用
<button>
按钮定义点击事件,在函数中通过$router
传入参数:
mineclk(){
this.$router.push({
path:'/mine,
query:{
name:'wht',
age:18
}
})
}
在页面中可以通过$route
获取query的值:
{{this.$route.query}}
{{this.$route.query.name}}
效果如下:
三、NavigationGuard导航守卫
3.1 全局守卫
如果需要修改页面的标题,在一个一个页面中通过生命周期函数修改会太麻烦,所以借助导航守卫。
全局守卫分为beforeEach()
(页面跳转前回调)和afterEach()
(页面跳转后回调)。
- 首先在
index.js
路由中通过meta
元数据来定义一些标题,比如:
{
path:"/mine",
component: () => import('../components/Mine'),
meta:{
title:"我的"
}
}
- 利用
beforeEach()
函数在跳转前置回调,修改标题:
router.beforeEach((to, from, next) => {
document.title = to.matched[0].meta.title
next()
})
(无法显示:router需要const一个参数,不能直接导出)
函数解析,可以看做为前置钩子(hook):
- to:即将要进入的路由对象;
- from:当前导航即将要离开的路由对象;
- next:调用方法后,才能进入下一个导航守卫
- matched:针对有嵌套的路由,选取第一个meta作为值
- 后置钩子的写法如下:
router.afterEach((to, from) => { })
3.2 路由独享守卫
在Router实例中的routes对象中配置:
beforeEnter:(to, from, next) => { }
3.3 组件内守卫
在组件内,可配置如下守卫:
beforeRouteEnter:(to, from, next) => { }
beforeRouteUpdate:(to, from, next) => { }
beforeRouteLeave:(to, from, next) => { }
具体可参考官方教程
四、生命周期函数
在组件中,常用的有以下三种生命周期函数:
create()
:创建组件的时候回调的函数;mounted()
:当组件的template挂载到DOM上会回调的函数;updated()
:当页面刷新时会回调的函数。
除此之外,可以用下图很好的表示:
五、keep-alive
使用<keep-alive>
将动态的组件包裹起来,可以缓存组件实例,从而不会进行销毁(destroyed
)。
当被包裹的组件动态切换时,keep-alive中的activated
和deactivated
这两个生命周期钩子函数将会被对应执行。
一个例子:
- 在
App.js
中,将动态显示的<router-view>
使用<keep-alive>
包裹起来:
<keep-alive><router-view></router-view></keep-alive>
- 在某个组件页面实例中,写上内部的
activated
和deactivated
函数:
<script>
export default {
name:'prod',
data () {
return {
};
},
created() {
console.log('created');
},
destroyed() {
console.log('destroyed');
},
activated() {
console.log('activated');
},
deactivated() {
console.log('deactivated');
},
}
</script>
- 运行,进入并离开组件页面,会发现没有执行
destroyed
函数:
5.1 keep-alive属性
keep-alive有2个重要属性:
include
-字符串或正则表达式匹配到的组件才会被缓存exclude
-字符串或正则表达式匹配到的组件不会被缓存
例如:
<keep-alive exclude="mine">
<router-view></router-view>
</keep-alive>
此时,组件名为mine
的组件就不会被缓存,失去alive后生命周期会执行销毁。