Vue Router
什么是前端路由
一、路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动.
路由器提供了两种机制: 路由和转送.
路由是决定数据包从来源到目的地的路径.
转送将输入端的数据转移到合适的输出端.
路由中有一个非常重要的概念叫路由表.
路由表本质上就是一个映射表, 决定了数据包的指向.
二、前端渲染和后端渲染
1、后端渲染
如jsp,后端渲染好网页再传给前端
2、后端路由
后端处理url和页面之间的映射关系
3、前后端分离
后端只负责提供数据,不负责任何阶段的内容
ajax出现后出现的前后端分离模式
后端只提供api来返回数据,前端通过ajax获取数据,并且可以通过JavaScript将数据渲染到页面
ios和android出现后,后端不需要进行任何处理,依然使用之前的一套api即可
4、前端渲染
浏览器中显示的网页中的大部分内容,都是由前端写的js代码在浏览器中执行,最终渲染出来的网页。
5、单页面富(SPA)应用阶段
在前后端分离的基础上加了一层前端路由
也就是前端来维护一套路由规则
整个网站只有一个html页面
三、url的hash和html的history
前端路由的核心:改变url,但是页面不进行整体的刷新
1、依靠改变url的hash实现
URL的hash也就是锚点(#), 本质上是改变window.location的href属性.
我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新
2、Html5的history.pushState()实现
history.pushState({},'','home')
3、Html5的history.replaceState()实现
由于是替换,所以,无法再浏览器中返回
4、Html5的history.go()实现
此外还有
history.go(n):
history.back():等价于history.go(-1)
history.forward():等价于history.go(1)
路由的基本配置
路由的安装
npm install vue-router --save
使用步骤
1.创建路由组件
在组件文件夹中创建Home.vue和About.vue组件模板
Home.vue
<template>
<div>
<h2>我是首页</h2>
<h4>我是首页内容,哈哈哈哈</h4>
</div>
</template>
<script>
export default{
name:"Home"//模板名称
}
</script>
<style>
</style>
About.vue
<template>
<div>
<h2>我是关于</h2>
<h4>我是关于页的内容,呵呵呵呵</h4>
</div>
</template>
<script>
export default{
name:'About'//模板名称
}
</script>
<style>
</style>
2.配置组件和路径的映射关系
在router目录下的index.js中进行配置
要先导入home和about两个组件模板
创建router对象数组
import Vue from 'vue'
import Router from 'vue-router'
//导入home和about两个模板文件
import Home from '../components/Home'
import About from '../components/About'
//1、通过Vue.use(插件),安装插件
Vue.use(Router);
//2、创建Router对象
const routes = [
//一个对象就是一个映射关系
{
path: '/home',//路径对应根/home
component:Home
},
{
path: '/about',//路径对应根/about
component:About
}
];
const router = new Router({
routes
});
export default router;
3.使用路由
main.js引入
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
render: h => h(App)
})
通过和
在App.vue模板文件中插入刚才创建并配置好的路由组件
router-link是vue默认的全局组件,可以只接拿来使用
和用来控制切换
同时添加to属性,指向各模板文件对应的路径
用于显示切换过来的自定义组件(home和about)
App.vue
<template>
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
components: {
}
}
</script>
<style>
</style>
细节处理
路由的默认路径和history
让路径默认跳转到首页,并且router-view渲染首页文件
只要多配置一个映射即可
//设置默认首页
const routes ={
path: '/',
component:Home
}
// 但更简洁的方式是使用重定向到/home映射上去
const routes = {
path: '/',
redirect:'/home'
}
在index.js中直接修改路由的配置模式为history(不显示路径中的#)
const router = new Router({
routes,
//配置浏览器地址栏模式
mode:'history'
});
export default router;
router-link补充
还有一些其他属性:
tag: tag可以指定之后渲染成什么组件, 比如上面的代码会被渲染成一个
- 元素, 而不是
replace: replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中
active-class: 当对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称. -
<router-link to="/home" tag="button" replace active-class="active">首页</router-link>
修改linkActiveClass的名称
在index.js中修改
const router = new VueRouter({ *// 配置路由和组件之间的应用关系* routes, mode: 'history', linkActiveClass: 'active' })
路由代码跳转
<template> <div id="app"> <router-link to="/home" tag="button">首页</router-link> <router-link to="/about" tag="button">关于</router-link> <router-view></router-view> <button @click="homeClick">首页</button> <button @click="aboutClick">关于</button> </div> </template> <script> export default { name: 'App', methods: { homeClick(){ //通过代码的方式修改路径,最终要通过router-link来实现 this.$router.push('/home').catch(err => err) }, aboutClick(){ if (this.$route.path != "/about") { this.$router.replace("/about"); } } }, } </script> <style> .myactive {color:red} </style>
路由的懒加载
- 由于打包后的JavaScript非常大,需要分割开来进行加载
- 生产环境一个路由对应一个页面级组件,对应打包一个js文件
- 这样可以需要使用的时候再加载
- 懒加载在注册组件的时候在import进来
即时加载的代码 - 这样打包的时候就分开文件打包了
// 配置路由相关的信息 import VueRouter from 'vue-router' import Vue from 'vue' // import Home from '../components/Home' // import About from '../components/About' // import User from '../components/User' const Home = () => import('../components/Home') const About = () => import('../components/About') const User = () => import('../components/User') // 1.通过Vue.use(插件), 安装插件 Vue.use(VueRouter) // 2.创建VueRouter对象 const routes = [ { path: '', // redirect重定向 redirect: '/home' }, { path: '/home', component: Home }, { path: '/about', component: About, }, { path: '/user/:id', component: User, } ] const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes, mode: 'history', linkActiveClass: 'active' }) // 3.将router对象传入到Vue实例 export default router
懒加载的方式
方式一: 结合Vue的异步组件和Webpack的代码分析.
const Home = resolve => { require.ensure([’…/components/Home.vue’], () => { resolve(require(’…/components/Home.vue’)) })};
方式二: AMD写法
const About = resolve => require([’…/components/About.vue’], resolve);
方式三: 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.
const Home = () => import(’…/components/Home.vue’)
动态路由
实现组件页面动态绑定一个id页面
创建一个模板页面user.vue
<template> <div> <h2>我是用户页面</h2> <p>用户页面的内容</p> </div> </template> <script> export default { name:'User', } </script> <style> </style>
修改主模板页面.vue
数据部分:
这里手动指定一个userid:‘zhangsan‘的对象数据<script> export default { name: 'App', data(){ return { userid:'zhangsan' } }, methods: { homeClick(){ //通过代码的方式修改路径,最终要通过router-link来实现 this.$router.push('/home') }, aboutClick(){ this.$router.replace('/about') } }, }
模板部分:
需要绑定数据,注意绑定格式,/user/是原始字符串,要用单引号括住再连接data数据userid
<router-link :to="'/user/'+userid" tag="button">用户</router-link>
完整app.vue页面
<template> <div id="app"> <router-link to="/home" tag="button">首页</router-link> <router-link to="/about" tag="button">关于</router-link> <router-link :to="'/user/'+userid" tag="button">用户</router-link> <router-view></router-view> <button @click="homeClick">首页</button> <button @click="aboutClick">关于</button> </div> </template> <script> export default { name: 'App', data(){ return { userid:'zhangsan' } }, methods: { homeClick(){ //通过代码的方式修改路径,最终要通过router-link来实现 this.$router.push('/home') }, aboutClick(){ this.$router.replace('/about') } }, } </script> <style> .myactive {color:red} </style>
修改index.js添加映射关系
在index中导入User组件在路径中需要连接userid,格式为:userid
{ path: '/user/:userid',//路径对应根/user component:User },
index.js完整代码
import Vue from 'vue' import Router from 'vue-router' //导入home和about两个模板文件 import Home from '../components/Home' import About from '../components/About' import User from '../components/User' //1、通过Vue.use(插件),安装插件 Vue.use(Router); //2、创建Router对象 const routes = [ //一个对象就是一个映射关系 { path: '/home',//路径对应根/home component:Home }, { path: '/about',//路径对应根/about component:About }, { path: '/user/:userid',//路径对应根/user component:User }, //设置默认首页 { path: '/', redirect:'/home' } ]; const router = new Router({ routes, //配置浏览器地址栏模式 mode: 'history', //设置active-class linkActiveClass:'myactive' }); export default router;
使用计算属性动态绑定页面数据
user.vue页面,
注意这里使用了this.$route.param来拿当前路由参数来那当前路由参数
<template> <div> <h2>我是用户页面</h2> <p>用户页面的内容</p> <div>userid</div> <div>$route.params.userid</div> </div> </template> <script> export default { name:'User', computed:{ userid(){ //$router拿到的是index.js页面中定义的routes对象数组 //this.$router是当前活跃路由 //注意这里使用的事$route不是之前使用的$router return this.$route.params.userid } }, } </script> <style> </style>
参数传递
传递参数主要有两种类型: params和query
params的类型:
配置路由格式: /router/:id
传递的方式: 在path后面跟上对应的值
传递后形成的路径: /router/123, /router/abc
query的类型:
配置路由格式: /router, 也就是普通配置
传递的方式: 对象中使用query的key作为传递方式<button @click=“profileClick”>档案
profileClick() { this.$router.push({ path: '/profile', query: { name: 'kobe', age: 19, height: 1.87 } }) }
传递后形成的路径: /router?id=123, /router?id=abc
r o u t e 和 route和 route和router是有区别的
r o u t e r 为 V u e R o u t e r 实 例 , 想 要 导 航 到 不 同 U R L , 则 使 用 router为VueRouter实例,想要导航到不同URL,则使用 router为VueRouter实例,想要导航到不同URL,则使用router.push方法
$route为当前router跳转对象里面可以获取name、path、query、params等路由嵌套
类似于一个路径分成两个部分(频道/子频道)如/home/news和/home/message访问一些内容
一个路径映射一个组件,访问这个两个路径分别渲染两个组件创建对应的字组件,并且在路由映射中配置对应的子路由
创建两个新的子路由模板HomeNews.vue及HomeMessage.vue
HomeNews.vue
<template> <div> <ul> <li>新的一条消息</li> <li>老的一条消息</li> <li>看过的消息</li> <li>没看过的消息</li> </ul> </div> </template> <script> export default { name:'HomeNews' } </script> <style> </style>
*HomeMessage.vue
<template> <div> <ul> <li>教育部新闻</li> <li>工信部新闻</li> <li>农业农村部新闻</li> <li>社会保障部新闻</li> </ul> </div> </template> <script> export default { name:'HomeMessage' } </script> <style> </style>
在主路由index.jx中导入模板对象并在路由对象中添加子路由(嵌套添加)
通过添加children数组的形式添加子路由
- 同时可以设置默认子路由的显示
const HomeNews = () => import('../components/HomeNews'); const HomeMessage = () => import('../components/HomeMessage');
const routes = [ //一个对象就是一个映射关系 { path: '/home',//路径对应根/home component: Home, //添加嵌套子路由 children: [ { path: 'news',//由于是子路由,不用再加斜杠/了 component:HomeNews }, { path: 'message',//由于是子路由,不用再加斜杠/了 component:HomeMessage }, //设置默认首页 { path: '', redirect:'/home/news' } ] }, { path: '/about',//路径对应根/about component:About }, { path: '/user/:userid',//路径对应根/user component:User }, //设置默认首页 { path: '/', redirect:'/home' } ];
在父路由组件内部使用标签
由于子路由是在Home路由模板中应用,应当在Home路由模板上添加子和
<template> <div> <h2>我是首页</h2> <h4>我是首页内容</h4> <router-link to='/home/news'>新闻</router-link> <router-link to='/home/message'>消息</router-link> <router-view></router-view> </div> </template> <script> export default{ name:"Home"//模板名称 } </script> <style> </style>
导航守卫
实际上就是个回调函数
利用声明周期的钩子函数来修改组件的title
使用beforeEach()监听路由跳转- 1、给每个路由添加一个meta对象,并设定一个title
meta: { title:'用户' }
2、使用beforeEach添加导航守卫
beforeEach()需要三个参数,to,from,netxt:
to.matched[0]是因为参数了路由嵌套,导致第一个链接拿不到meta元素
因此要去拿上一级的matched标签数组,第一个元素包含多了meta元素。在进一步拿到title,最后去修改document的title
导航钩子的三个参数解析:
to: 即将要进入的目标的路由对象.
from: 当前导航即将要离开的路由对象.- next: 调用该方法后, 才能进入下一个钩子.
router.beforeEach(function (to, from, next) { //使用目标(to)组件的meta.title修改当前文档的title标签 document.title = to.matched[0].meta.title; console.log(to); //调用父级next() next(); })
后置钩子
beforeEach是前置钩子,afterEach后置钩子,不需要主动调用next()进入到下一个钩子//后置钩子,不需要主动调用next()函数,只有连个参数 router.afterEach((to,from) => { console.log('------'); })
独享守卫和组件内的守卫(guard)
keep-alive及其他问题
keep-alive是vue的一个内置组件,在这个标签内的所有vue组件呈现保留状态(即不会被重新渲染)
有两个非常重要的属性include:字符串或正则表达式,只有匹配的组件会被缓存
exclude:字符串或正则表达式,任何匹配的组件都不会被缓存包含<keep-alive> <router-view> //所有匹配的组件都会被缓存 </router-view> </keep-alive>
排除(exclude中逗号后面不能空格)
正则表达式中逗号后面也不要空格
<keep-alive exclude='Profile,User'> <router-view> //所有匹配的组件都会被缓存 </router-view> </keep-alive>
router-view也是一个组件,如果被直接抱在keep-alive里面,所有路径匹配到的视图组件都会被缓存(不会被重新渲染)
通过create生命周期函数(钩子函数)来验证
home.vue
<template> <div> <h2>我是首页</h2> <p>我是首页内容, 哈哈哈</p> <router-link to="/home/news">新闻</router-link> <router-link to="/home/message">消息</router-link> <router-view></router-view> <h2>{{message}}</h2> </div> </template> <script> export default { name: "Home", data() { return { message: '你好啊', path: '/home/news' } }, created() { console.log('home created'); }, destroyed() { console.log('home destroyed'); }, // 这两个函数, 只有该组件被保持了状态使用了keep-alive时, 才是有效的 activated() { this.$router.push(this.path); console.log('activated'); }, deactivated() { console.log('deactivated'); }, beforeRouteLeave (to, from, next) { console.log(this.$route.path); this.path = this.$route.path; next() } } </script> <style scoped> </style>