七、路由
一、路由的概念
路由的本质就是一种对应关系
,根据不同的URL请求,返回对应不同的资源。
路由分为前端路由和后端路由:
-
前端路由
-
根据不同的事件来显示页面的内容,是事件与事件处理函数之间的对应关系
-
本质:事件与事件处理函数之间的对应关系
-
-
后端路由
-
由服务器端进行实现并实现资源映射分发
-
本质:URL请求地址与服务器资源之间的对应关系
-
二、前端路由模式
1、hash模式
-
hash路由模式:http://xxx.abc.com/#/xx,带有#号,后面就是hash值的变化
-
改变后面的hash值,它不会向服务器发出请求,因此也就不会刷新页面
-
每次hash值发生改变的时候,会触发hashchange事件,通过监听该事件,来知道hash值发生了哪些变化
1 window.addEventListener('hashchange', ()=>{ 2 // 通过 location.hash 获取到最新的 hash 值 3 console.log(location.hash); 4 });
2、history模式
HTML5的History API为浏览器的全局history对象增加了该扩展方法。它是一个浏览器(bom)的一个接口,在window对象中提供了onpopstate事件来监听历史栈的改变,只要历史栈有信息发生改变的话,就会触发该事件。
1 history.pushState({},title,url); // 向历史记录中追加一条记录 2 history.replaceState({},title,url); // 替换当前页在历史记录中的信息。 3 window.addEventListener('popstate', function(event) { 4 console.log(event) 5 })
三、vue-router
1、安装vue-router
如果在vue-cli创建项目时没有勾选上vue-router
选项,此时就需要手动来安装它(切记,要进入项目中再去运行这个指令):
npm i -S vue-router
在package.json
文件中dependencies
选项中查看是否安装成功
2、路由文件的位置
-
位置:
src/router/index.js
-
若创建项目时没有选择
vue-router
选项,需要自行创建
index.js:
1 import Vue from 'vue' 2 import VueRouter from 'vue-router' 3 4 // 引入组件 5 import Home from '../views/Home.vue' 6 7 // 使用vue-router 8 Vue.use(VueRouter) 9 10 // 定义路由规则 11 const routes = [ 12 { 13 path: '/', 14 name: 'Home', 15 component: Home 16 }, 17 { 18 path: '/about', 19 name: 'About', 20 // route level code-splitting 21 // this generates a separate chunk (about.[hash].js) for this route 22 // which is lazy-loaded when the route is visited. 23 component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 24 } 25 ] 26 27 // 创建路由实例 28 const router = new VueRouter({ 29 mode: 'history', 30 base: process.env.BASE_URL, 31 routes 32 }) 33 34 export default router
3、路由懒加载:
需要加载时再加载,而不是一开始全部加载
-
写法一
1 import Vue from 'vue' 2 import VueRouter from 'vue-router' 3 4 // 使用vue-router 5 Vue.use(VueRouter) 6 7 // 路由懒加载 8 const Home = () => import{'../views/Home.vue'} 9 const About = () => import{'../views/About.vue'} 10 11 // 定义路由规则 12 const routes = [ 13 { 14 path: '/', 15 name: 'Home', 16 component: Home 17 }, 18 { 19 path: '/about', 20 name: 'About', 21 component: About 22 } 23 ] 24 25 // 创建路由实例 26 const router = new VueRouter({ 27 mode: 'history', 28 base: process.env.BASE_URL, 29 routes 30 }) 31 32 export default router
- 写法二
1 import Vue from 'vue' 2 import VueRouter from 'vue-router' 3 4 // 使用vue-router 5 Vue.use(VueRouter) 6 7 // 定义路由规则 8 const routes = [ 9 { 10 path: '/', 11 name: 'Home', 12 component: () => import(/* webpackChunkName: "home" */ '../views/Home.vue') 13 }, 14 { 15 path: '/about', 16 name: 'About', 17 component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 18 } 19 ] 20 21 // 创建路由实例 22 const router = new VueRouter({ 23 mode: 'history', 24 base: process.env.BASE_URL, 25 routes 26 }) 27 28 export default router
4、在项目入口文件main.js
中引入router
1 import Vue from 'vue' 2 import App from './App.vue' 3 // 路径./router后面的index.js可以省略不写 4 import router from './router' 5 import store from './store' 6 7 Vue.config.productionTip = false 8 9 new Vue({ 10 router, 11 store, 12 render: h => h(App) 13 }).$mount('#app')
5、App.vue文件中使用路由
1 <template> 2 <div id="app"> 3 <div id="nav"> 4 <router-link to="/" tag="button" replace>Home</router-link> | 5 <router-link to="/about">About</router-link> 6 </div> 7 <router-view/> 8 </div> 9 </template> 10 11 <style lang="scss"> 12 #app { 13 font-family: Avenir, Helvetica, Arial, sans-serif; 14 -webkit-font-smoothing: antialiased; 15 -moz-osx-font-smoothing: grayscale; 16 text-align: center; 17 color: #2c3e50; 18 } 19 20 #nav { 21 padding: 30px; 22 23 a { 24 font-weight: bold; 25 color: #2c3e50; 26 27 &.router-link-exact-active { 28 color: #42b983; 29 } 30 } 31 } 32 </style>
<router-link></router-link>
和<router-view></router-view>
-
它俩是vue封装好的组件
-
router-link的to属性匹配跳转的路径
-
router-link的tag属性可以指定渲染成的元素
-
router-link的replace属性不会留下history记录,因此后退返回键无法使用
-
router-view用来显示组件的位置(类似占位的作用)
6、路由重定向
概念:用户在访问地址 A 的时候,强制用户跳转到地址 B ,从而展示特定的组件页面
实现: 通过路由规则的redirect
属性,指定一个新的路由地址,可以很方便地设置路由的重定向
1 const routes = [ 2 { path: '/', redirect: '/user' }, 3 { path: '/user', component: User }, 4 { 5 path: '/home', 6 name: 'Home', 7 component: Home 8 }, 9 { 10 path: '/about', 11 name: 'About', 12 // route level code-splitting 13 // this generates a separate chunk (about.[hash].js) for this route 14 // which is lazy-loaded when the route is visited. 15 component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 16 } 17 ]
7、嵌套路由
比如我们有一个/user
的路由,那么/users
下面还可以添加子级路由,
如:/user/name
、/user/age
等等,这样的路由情形称之为嵌套路由。
核心思想:在父路由组件的模板内容中添加子路由链接和子路由填充位(占坑),同时在路由规则处为父路由配置children属性指定子路由规则:
1 routes: [ 2 { 3 path: "/user", 4 component: User, //这个不能丢 5 // 通过children属性为/user添加子路由规则 6 children:[ 7 { path: "name", component: Name }, 8 { path: "age", component: Age }, 9 ] 10 } 11 ]
1 <!-- 需要在 User组件中定义一个router-view 用于嵌套路由的渲染显示 --> 2 <router-view></router-view>
8、动态路由
所谓动态路由就是路由规则中有部分规则是动态变化的,不是固定的值,需要去匹配取出数据(即路由参数
)。
1 // 传递参数id 2 const router = new VueRouter({ 3 // routes是路由规则数组 4 routes: [ 5 { path: '/user/:id', component: User }, 6 // 此处的“:”只是在声明的时候写,在使用的时候不需要写“:” 7 ] 8 }) 9 10 // 组件中获取id值(注意拼写) 11 const User = { 12 template: '<div>User ID is {{$route.params.id}}</div>' 13 }
使用:
1 export default{ 2 methods: { 3 show: function(id){ 4 this.$router.push('/film/showdetail/' + id) 5 } 6 } 7 } 8 // 如果路由规则里声明需要传递参数,但是实际使用的时候没传递参数会影响落地页的显示,显示成白板(但是不报错)。
{ path: "showdetail/:id?", component: ShowDetail },
9、命名路由
顾名思义就是给路由起名字(第2小节代码routes数组中对象的name属性)
10、参数传递
父组件
1 <template> 2 <div id="app"> 3 <router-link :to="{path: '/profile', query: {name: 'zhangsan', age: 18}}">profile</router-link> 4 <router-view/> 5 </div> 6 </template>
子组件接收
1 <template> 2 <div id="profile"> 3 <div>{{$route.query.name}}</div> 4 <div>{{$route.query.age}}</div> 5 </div> 6 </template>
11、路由守卫
导航守卫
就是路由跳转过程中的一些钩子函数
,这个过程中触发的这些函数能让你操作一些其它事情时,可以进行过滤操作,这就是导航守卫。
-
全局守卫
1 // 全局前置守卫 路由规则文件中定义 2 // 当一个导航触发时,触发前置守卫, 3 // to: Route: 即将要进入的目标 路由对象 4 // from: Route: 当前导航正要离开的路由 5 // next: Function: 一定要调用该next方法,否则路由不向下执行。 6 router.beforeEach((to, from, next) => { 7 // ... 8 }) 9 10 // 全局后置钩子 11 // 此钩子不会接受 next 函数也不会改变导航本身 12 router.afterEach((to, from) => { 13 // ... 14 })
- 组件内守卫
1 // 可以在路由组件内直接定义以下路由导航守卫,手动更改地址栏中的路由不会触发`beforeRouteUpdate`和`beforeRouteLeave`钩子函数。 2 const Foo = { 3 template: `...`, 4 beforeRouteEnter (to, from, next) { 5 // 在渲染该组件的对应路由被 confirm 前调用 6 // 不!能!获取组件实例 `this`(这个时候还没有进入到to对应的组件中,所以拿不到this) 7 // 因为当守卫执行前,组件实例还没被创建 8 }, 9 beforeRouteUpdate (to, from, next) { 10 // 在当前路由改变,但是该组件被复用时调用 11 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, 12 // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 13 // 可以访问组件实例 `this` 14 }, 15 beforeRouteLeave (to, from, next) { 16 // 导航离开该组件的对应路由时调用 17 // 可以访问组件实例 `this` 18 } 19 }
四、导航方式
1、声明式导航
通过点击链接实现的导航方式,例如HTML中的“<a>”标签,Vue中的“<router-link>”所实现的。
1 <router-link to="path">xxx</router-link> 2 <!-- 3 to 要跳转到的路由规则 string|object 4 to="users" 5 :to="{path:'path'}" 6 -->
2、编程式导航
通过调用JavaScript形式API实现的导航方式,例如location.href实现的跳转效果
1 this.$router.push("/login"); 2 this.$router.push({ path:"/login" }); 3 this.$router.replace({ path:"/login" }); 4 this.$router.push({ path:"/login", query:{ username:"jack" }}); 5 this.$router.push({ name:'user', params: { id:123 }}); 6 this.$router.go( n );//n为数字 负数为回退
注:编程式导航在跳转到与当前地址一致的URL时会报错,但这个报错不影响功能,可以在路由入口文件index.js
中添加如下代码解决该问题
1 const originalPush = VueRouter.prototype.push; 2 VueRouter.prototype.push = function push(location) { 3 return originalPush.call(this, location).catch((err) => err); 4 };