Vue Router
1、什么是路由?
根据用户给出的url地址,导航到不同组件中;
2、vue router实现原理
SPA:单一页面应用程序
,只有一个完整的页面,它在加载页面时,不会加载整个界面,而是只更新某个指定的容器中的内容。单页面应用(SPA)的核心之一是:更新视图而不是更新请求页面
。Vue Router在实现单页面前端路由时,提供了两种方式:hash模式和history模式,默认使用的是hash模式。
2.1、hash模式
- url hash就是类似于:localhost:8080/#/login
这种#后面hsah值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。而每次hash值的变化,还会触发hashchange事件,通过这个时间我们就可以知道hash值发生了哪些变化。我们便可以监听hashchange来实现更新页面部分的内容的操作。因此改变hash不会重新加载页面
function matchAndUpdate(){
}
window.addEventListener("hashchange",matchAndUpdate)
2.2、history模式
因为HTML5标准发布,多了两个API, pushState 和replaceState,通过这两个API可以改变url地址且不会发送请求。同时还有popstate事件。通过这些就能用另一种方式来实现前端路由了,但原理都是跟hash实现相同的。用了HTML5的实现,单页路由的url就不会多出一个#
,变得更加美观。但因为没有#号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。
function matchAndUpdate(){
}
window.addEventListener("popstate",matchAndUpdate)
3、路由基础
3.1、路由列表
const router = new VueRouter({
路由列表:是一个数组
routes:[
单个路由
path:表示路由路径字符串,在path中需要/开头,目的使全局路由对象能够监听到某个用户请求的路径;
component:组件,如果path与用户访问的路径匹配,则去查找相应的组件,让组件渲染页面
{
path:/login,
component:Login
}
]
})
3.2、路由标签
<router-link>标签,进行路由导航,它会被Vue解析为一个超链接;
to属性:配置路由路径;
<router-link to="/login">登录</router-link>(声明式导航)
通过router-link标签导航之后,组件如何渲染到页面中?
<router-view/>:渲染某个路由路径对应的组件,称为路由出口
4、动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个User组件,对于所有ID各不相同的用户,都要使用这个组件来渲染。那么,我们可以在vue-router的路由路径中使用动态路径参数”来达到这个效果:当我们在地址后面直接添加任意字符,我们会发现文档内容随着我们的更改而改变。
4.1、动态路由路径
{
path:/user/:id,//通过$route.params.id可以获取到这个值,如果使用path:/user-* 通配符,是无法获取的
component:Login
}
<router-link to="/user/1001">登录</router-link>
<router-link to="/user/1002">登录</router-link>
//传递多个参数
{
path:/user/:id/:uerName,//通过$route.params.id可以获取到这个值,如果使用path:/user-* 通配符,是无法获取的
component:Login
}
this.$router.push(`/user/${id}/${name}`)
4.2、响应(监听)动态路由参数的变化
//侦听器:侦听当前路由的变化
watch: {
//to:导航之后(跳转之后)的路由对象
//from:旧值,跳转前的路由对象
"$route":(to, from)=> {
console.log("导航之后",to);
console.log("前",from);
}
},
官网上是这样写的
created() {
//侦听器:侦听当前路由的变化
this.$watch(() => this.$route.params,
(toParams, previousParams) => {
// 对路由变化做出响应...
console.log(toParams);
console.log(previousParams);
})
},
4.3、捕获所有路径(路由)
路由路径通配符*。
路由列表的匹配模式是自上而下的逐个路由匹配,带有通配符的路由一定放在路由列表最后。
{
path: "/*",
component: Error
}
5、嵌套路由
{
path: '/about',
component:About,
children: [{
//嵌套路由列表
// path:"list",//相对路径是上级路由作为前缀路径 to="/about/list"
path:"/list",//绝对路径不能以上级路由作为前缀路径 to="/list"
component:Child
}]
},
6、编程式导航
6.1、push
//字符串形式
this.$router.push('/vuex');
//对象形式
this.$router.push({path:'/vuex'})
路由在导航之后会形成历史记录history
;
push:在历史列表中追加一个
6.2、replace
replace:在历史列表中替换上一个记录
,不可以后退到上一个页面;
在声明式导航中的应用:
<router-link to="xxxx" replace>xxx</router-link>
编程式导航:
this.$router.replace('/vuex')
一般可以用在登录后,跳转到主页,就用replace,防止用户回退时回退到登录页面。
6.3、go
历史前进或后退多少步;
this.$router.go(-3)
7、路由配置
7.1、命名路由
为每个路由的配置添加一个name属性,且必须唯一;
{
path: "/login",
name:"login",//路由名称
component: Login
}
根据路由名称进行导航
//声明式
<router-link :to="{name:'vuex'}" replace>xxx</router-link>
//编程式导航(对象形式,不能是字符串形式)
this.$router.push({name:'vuex'})
7.2、重定向redirect
通过重定向redirect
是实现组件的复用;
{
path: "/",
redirect:"/home",
//redirect: {name: "vuex"}也可以通过组件名称进行重定向
},
{
path: '/home',
name: 'home',
component: HomeView
}
7.3、别名alias
{
path: '/home',
name: 'home',
component: HomeView,
alias:"/hm"//路由别名
},
7.7、其他
export default new Router({
mode: 'history', //路由模式,取值为history与hash
base: '/', //打包路径,默认为/,可以修改
routes: [
{
path: string, //路径
ccomponent: Component; //页面组件
name: string; // 命名路由-路由名称
components: { [name: string]: Component }; // 命名视图组件
redirect: string | Location | Function; // 重定向
props: boolean | string | Function; // 路由组件传递参数
alias: string | Array<string>; // 路由别名
children: Array<RouteConfig>; // 嵌套子路由
// 路由单独钩子
beforeEnter?: (to: Route, from: Route, next: Function) => void;
meta: any; // 自定义标签属性,比如:是否需要登录
icon: any; // 图标
// 2.6.0+
caseSensitive: boolean; // 匹配规则是否大小写敏感?(默认值:false)
pathToRegexpOptions: Object; // 编译正则的选项
}
]
})
8、路由传参
两个组件之间的数据传递;
8.1、query
- 字符串拼接形式
<router-link to="/VModel?name=user&pwd=123"></router-link>
//接收参数
this.$route.query
- 以对象的形式
//传递的是一个对象,也可以拼接为字符串形式
this.$router.push({path:'/vuex',query:{name:'111',age:18}})
//接收参数
this.$route.query
8.2、params
- 动态路由匹配传递参数(路径传参)
这种方式必须传参
{
path: '/vuex/:ids/:name',
name: 'vuex',
component: VueXStudy,
}
<router-link to="/vuex/1/xiao">xxxx</router-link>
//接收参数
this.$route.params
- 编程式导航传参
- 参数隐藏,不在路径中出现
- 路径固定
- 必须通过name导航
this.$router.push({name:'vuex',params:{name:'张三',age:18}})
//接收参数
this.$route.params
- 声明式导航传参
<router-link :to="{name:'vuex',params:{name:'李四'}}">xxxx</router-link>
//接收参数
this.$route.params
8.3、props
在组件中使用$route会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的url使用,限制了其灵活性。
解耦
{
//路由路径中的占位符名称必须与组件props变量名称一致
path: '/vuex/:id/:userName',
name: 'vuex',
component:VueXStudy,
//在路径配置中开启props传参
props: true
}
<router-link to="/vuex/1001/王麻子">xxxx</router-link>
//定义props对象接收路由参数
props:{
id:String,
userName:String
}
8.4、query 和 params的区别
- query 可以使用path和name,
params只能使用name
; - query传参
刷新后可以保存
,params不会保存; - query传递的参数会
显示在url地址中
,params不会显示; - 通过字符串拼接的形式传递参数,使用的是$route.query接收参数;
- query不可以和
动态路由匹配
一起使用,而使用params接收参数
;
9、导航守卫
“导航”表示路由正在发生变化;
类似于javaweb里面的过滤器,拦截器
9.1、全局前置守卫
router.beforeEach()一般用来做一些进入页面的限制。比如没有登录,就不能进入某些页面,只有登录了之后才有权限查看某些页面。说白了就是路由跳转前根据业务逻辑判断是否拦截。
router.beforeEach(回调函数),回调函数中的三个参数
- to:将要导航到的路由对象
- from:导航之前的路由对象
- next:导航函数,在函数执行时,此函数必须执行一次,且仅能执行一次
next():无参数调用就是对导航的放行;
next(阻止导航,阻止导航后跳转的路径):阻止导航,阻止路由跳转
//有参数时编写的形式:
next( '/login')
next({path: '/login'})
next({name: 'login'})
//前置守卫
//模拟身份认证:判断用户是否登录,如果没有登录,跳转至登录页面
router.beforeEach((to, from, next) => {//路由跳转中
if (to.path == "/") {//"/"默认进入登录页面
next();
} else {
if (sessionStorage.getItem("userInfo")) {
next()
} else {
next({path: '/'})
}
}
})
9.2、路由独享守卫
守卫范围:当前路由以及当前路由下的所有嵌套路由;
{
path: '/vuex',
name: 'vuex',
component: () => import( '../components/vuex/VueXStudy.vue'),
beforeEnter: (to, from, next) => {
console.log("to", to);
console.log("from", from);
next();
}
}
10、路由懒加载
未使用懒加载
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter)
export default new VueRouter({
routes:[
{
path: '/home',
name: 'home',
component: HomeView,
}
]
})
10.1、vue异步组件实现懒加载
方法如下:component:resolve=>(require(['需要加载的路由的地址']),resolve)
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default new VueRouter({
routes:[
{
path: '/home',
name: 'home',
component: resolve=>(require(["../views/HomeView.vue"],resolve)),
}
]
})
10.2、ES6 提出的import方法(最常用,推荐使用)
方法如下: component: () =>import('需要加载的模块地址')
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default new VueRouter({
routes:[
{
path: '/home',
name: 'home',
component: () => import( '../views/HomeView.vue')
}
]
})
10.3、webpack提供的require.ensure()实现懒加载
- vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。
- 这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件
- require.ensure可实现按需加载资源,包括js,css等。他会给里面require的文件单独打包,不会和主文件打包在一起。
- 第一个参数是数组,表明第二个参数里需要依赖的模块,这些会提前加载。
- 第二个是回调函数,在这个回调函数里面require的文件会被单独打包成一个chunk,不会和主文件打包在一起,这样就生成了两个chunk,第一次加载时只加载主文件。
- 第三个参数是错误回调。
- 第四个参数是单独打包的chunk的文件名
11、vue2中动态添加路由router.addRoute
动态添加根路由:router.addRoute({path:xxx,name:xx....})
动态添加子路由:router.addRoute(parentName,{path:xxx,name:xx....})
,其中parentName为父级路由定义的name字段。