【 web高级 01vue 】 vue预习课11 路由

Vue Router

Vue RouterVue.js 官方的路由管理器。

一、安装

vue add router

二、基础

(一) 起步

路由规划、配置,router/index.js
商品列表(home) - 商品管理(about)

  • 路由出口、导航,App.vue
<nav>
	<router-link to="/">首页</router-link>
	<router-link to="/about">管理</router-link> 
</nav> 

<router-view></router-view>
  • router – index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    
    
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    
    
    path: '/admin',
    name: 'Admin',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    //懒加载
    //把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件
    //结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。
    //https://router.vuejs.org/zh/guide/advanced/lazy-loading.html
    component: () => import('../views/Admin.vue')
  }
]

const router = new VueRouter({
    
    
  routes
})

export default router

(二) 动态路由匹配

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:

{
    
     path: '/user/:id', component: User }

范例:查看课程详情

  • views/Detail.vue
<div> 
	<h2>detail page</h2> 
	<p>{
   
   {$route.params.name}}</p> 
</div>
  • router/index.js
{
    
     
	path: '/course/:name', component: () => import('../views/Detail.vue')
}
  • LIst.vue
<router-link :to="`/course/${c.name}`"> 
	{
   
   { c.name }} - {
   
   { c.price | currency('¥') }} 
</router-link>
2.2.1 捕获所有路由或 404 Not found 路由
{
    
     
	// 会匹配所有路径
	path: '*', component: () => import('../views/404.vue') 
}

(三) 嵌套路由

实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构
对应嵌套的各层组件,例如:

/user/foo/profile                            /user/foo/posts
+------------------+                        +-----------------+ 
|       User       |                        |       User      | 
| +--------------+ |                        | +-------------+ | 
| |    Profile   | |      +------------>    | |    Posts    | |
| |              | |                        | |             | |
| +--------------+ |                        | +-------------+ | 
+------------------+                        +-----------------+

范例:嵌套方式显示商品详情
在这里插入图片描述
router --> index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    
    
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    
    
    path: '/admin',
    name: 'Admin',
    component: () => import( '../views/Admin.vue'),
    children:[
      {
    
    
          path:'/detail/:name',
          name:'Detail',
          component: () => import( '../views/Detail.vue'),

      }
    ]
  }
]

const router = new VueRouter({
    
    
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

Admin.vue

<template>
  <div class="about">
    <h1>This is an admin page</h1>

    <div v-for="(item, index) in list" :key="index">
      <router-link  :to="`/detail/${item.id}`">
          <div>{
   
   { item.name }}  --  {
   
   {item.price}}</div>
      </router-link>
      
    </div>

    <router-view></router-view>

  </div>
</template>
<script>
export default {
     
     
  data() {
     
     
    return {
     
     
      list: [
        {
     
      name: "萝卜", price: 100, id:1 },
        {
     
      name: "白菜", price: 50, id:2 },
      ],
    };
  },
};
</script>

Detail.vue

响应路由参数变化

<template>
    <div>
        <h1>detail页面</h1>
        {
   
   {$route.params.id}}
    </div>
</template>
<script>
export default {
     
      
 	created () {
     
     
 		//detail页面变化的时候,地址是参数在变化,
 		//这个组件在创建创建的第一次,以后再切换的时候,发现不再发送
 		//内在的原因是组件的复用,为了提高程序的效率
 		//发现detail这个组件不需要重新创建,只是复用就可以了,这个组件不会在销毁,重新创建了。
		//没有机会让它多次去执行这个 created 的生命周期
		//解决方式:写一个对 $route 的监听器
		console.log('通过id,发送详情的请求');
    },
	watch: {
     
      //带选项的监听,第一次进来的时候获取一次数据,以后发生变化的时候依然重新调用,就不用再created中再写一次
		$route: {
     
      
			handler: () => {
     
      
				//发现 $route 发生变化了,就重新请求数据
				console.log("$route change----获取详情"); 
				//第一次请求的时候是没有的,但是在列表点击来回切换的时候可以看到
			},
			immediate: true //我们程序在 created 的时候执行一次
		} 
	}
};
</script>

在这里插入图片描述

(四) 编程导航

路由跳转

router.push(location, onComplete?, onAbort?)

// 字符串
router.push('home')

// 对象
router.push({
    
     path: 'home' })

// 命名的路由
router.push({
    
     name: 'user', params: {
    
     userId: '123' }})

// 带查询参数,变成 /register?plan=private
router.push({
    
     path: 'register', query: {
    
     plan: 'private' }})

(五) 命名路由

通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。

const router = new VueRouter({
    
    
  routes: [
    {
    
    
      path: '/user/:userId',
      name: 'user',
      component: User
    }
  ]
})

要链接到一个命名路由,可以给 router-linkto 属性传一个对象:

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

调用 router.push() 时:

router.push({
    
     name: 'user', params: {
    
     userId: 123 }})

三、进阶

(一) 路由守卫

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。

比如说有一些路由,用户没有权限的话,他不应当能看到这个路由所对应的内容,这个时候我们对这个路由进行保护,就显得非常重要了。

在这里插入图片描述
1.全局守卫

router --> index.js

// ......

const routes = [
  // ......
  {
    
    
    path: "/admin",
    name: "Admin",
    component: () => import("../views/Admin.vue"),
    meta: {
    
    
      auth: true,//admin需要路由守卫
    },
  },
  {
    
    
     // 会匹配所有路径
    path: '*',
    component: () => import("../views/NotFound.vue"),
  }
];

//   ......

//  to:
/*
to    去哪里
from  来自哪里
next  应不应该放行的next函数
*/
router.beforeEach((to, from, next) => {
    
    
 


  /*
  官网的例子
  if (to.name !== "Login" && !isAuthenticated) next({ name: "Login" });
  else next();
  
  */ 



  // 判断路由是否需要守卫
  // 通过 meta 数据的方式
  if(to.meta.auth){
    
      //判断到底需不需要守卫,
    //如果需要守卫,则判断用户是否登录
    //全局保存登录状态
    if(window.isLogin){
    
      
      //如果已经登录,放行
      next();
    }else{
    
    
      //没有登录,不可以放行,进入login页面
      next('/login?redirect=' + to.fullPath );//将来登录以后,重定向,还可以回到 我想去的 页面 。  to.fullPath :路由的完整路径,
    }

  }else{
    
    
    //如果不需要登录就可以看到的页面
    next();//不需要校验验证,可以直接访问。
  }
  
});

// ......

/*解决F12报错问题
//见下图
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch((err) => err);
};
*/

在这里插入图片描述

Login.vue

<template>
    <div>
        <button @click="login"   v-if="!isLogin">登录</button>
        <button @click="logout"  v-else>注销</button>
    </div>
</template>

<script>
    export default {
     
     
        computed: {
     
     
            isLogin() {
     
     
                return window.isLogin
            }
        },
        methods: {
     
     
            login() {
     
     
                window.isLogin = true;
                this.$router.push(this.$route.query.redirect);
                // console.log()
            },
            logout(){
     
     
                window.isLogin  = false;
                this.$router.push('/');
            }
        },   
    }
</script>


2.路由独享的守卫
你可以在路由配置上直接定义 beforeEnter 守卫:


{
    
    
    path: "/admin",
    name: "Admin",
    component: () => import("../views/Admin.vue"),
    beforeEnter: (to, from, next) => {
    
    
        if(window.isLogin){
    
      
          next();
        }else{
    
    
   
          next('/login?redirect=' + to.fullPath );
        }
    }
  },

3.组件内的守卫

Admin.vue

<template>
  ......
</template>
<script>
export default {
     
     
  beforeRouteEnter(to, from, next) {
     
     
    if (window.isLogin) {
     
     
      next();
    } else {
     
     
      next("/login?redirect=" + to.fullPath); 
    }
  },
};
</script>

(二) 数据获取

有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。

1.导航完成后获取数据

当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。

假设我们有一个 Post 组件,需要基于 $route.params.id 获取文章数据:

<template>
  <div class="post">
    <div v-if="loading" class="loading">
      Loading...
    </div>

    <div v-if="error" class="error">
      {
   
   { error }}
    </div>

    <div v-if="post" class="content">
      <h2>{
   
   { post.title }}</h2>
      <p>{
   
   { post.body }}</p>
    </div>
  </div>
</template>

export default {
    
    
  data () {
    
    
    return {
    
    
      loading: false,
      post: null,
      error: null
    }
  },
  created () {
    
    
    // 组件创建完后获取数据,
    // 此时 data 已经被 observed 了
    this.fetchData()
  },
  watch: {
    
    
    // 如果路由有变化,会再次执行该方法
    '$route': 'fetchData'
  },
  methods: {
    
    
    fetchData () {
    
    
      this.error = this.post = null
      this.loading = true
      // replace getPost with your data fetching util / API wrapper
      getPost(this.$route.params.id, (err, post) => {
    
    
        this.loading = false
        if (err) {
    
    
          this.error = err.toString()
        } else {
    
    
          this.post = post
        }
      })
    }
  }
}

2.在导航完成前获取数据
通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。

export default {
    
    
  data () {
    
    
    return {
    
    
      post: null,
      error: null
    }
  },
  beforeRouteEnter (to, from, next) {
    
    
    getPost(to.params.id, (err, post) => {
    
    
      next(vm => vm.setData(err, post))
    })
  },
  // 路由改变前,组件就已经渲染完了
  // 逻辑稍稍不同
  beforeRouteUpdate (to, from, next) {
    
    
    this.post = null
    getPost(to.params.id, (err, post) => {
    
    
      this.setData(err, post)
      next()
    })
  },
  methods: {
    
    
    setData (err, post) {
    
    
      if (err) {
    
    
        this.error = err.toString()
      } else {
    
    
        this.post = post
      }
    }
  }
}

(三) 动态路由

通过 router.addRoutes(routes) 方式动态添加路由

上面的路由守卫中,这些路由是提前已经配置好了,但是用户能不能进去,能不能看会做一个拦截判断。

如果这个人压根就没有权限看这个路由,能不能说根据当前用户的登录以后的角色信息,然后觉得他能看什么,我就在路由表加什么。

router -->  index.js
路由守卫修改为:要求用户必须登录,否则只能去登录页
......

const routes = [
  {
    
    
    path: "/",
    name: "Home",
    component: Home,
  },
  {
    
    
    path: "/login",
    name: "Login",
    component: () => import("../views/Login.vue"),
  },
  // {
    
    
  //   path: "/admin",
  //   name: "Admin",
  //   component: () => import("../views/Admin.vue"),
  //   children: [
  //     {
    
    
  //       path: "/admin/detail/:name",
  //       name: "Detail",
  //       component: () => import("../views/Detail.vue"),
  //     },
  //   ],
  //   // meta: {
    
    
  //   //   auth: true, //admin需要路由守卫
  //   // },
  // },

  {
    
    
    // 会匹配所有路径
    path: "*",
    component: () => import("../views/NotFound.vue"),
  },
];

const router = new VueRouter({
    
    
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});


//  to:
/*
to    去哪里
from  来自哪里
next  应不应该放行的next函数.
强制用户去登录
*/
router.beforeEach((to, from, next) => {
    
    
  //判断逻辑

  //1.是否登录
  if (window.isLogin) {
    
    
    //如果已经登录,进一步判断

    if (to.path === "/login") {
    
    
      //如果去登录页,去首页
      next("/");
    } else {
    
    
      //如果去的不是登录页,则放行
      next();
    }
  } else {
    
    
    //没有登录
    if (to.path === "/login") {
    
    
      //如果去登录页,就放行
      next();
    } else {
    
    
      //如果去的不是登录页,必须去登录页面
      next("/login?redirect=" + to.fullPath);
    }
  }
});

......

Login.vue


Login.vue用户登录成功后动态添加/about

<template>
  <div>
    <button @click="login" v-if="!isLogin">登录</button>
    <button @click="logout" v-else>注销</button>
  </div>
</template>

<script>
export default {
     
     
  computed: {
     
     
    isLogin() {
     
     
      return window.isLogin;
    },
  },
  methods: {
     
     
    login() {
     
     
      window.isLogin = true;

      //动态添加路由
      this.$router.addRoutes([
        {
     
     
          path: "/admin",
          name: "Admin",
          component: () => import("../views/Admin.vue"),
          children: [
            {
     
     
              path: "/admin/detail/:name",
              name: "Detail",
              component: () => import("../views/Detail.vue"),
            },
          ],
        },
      ]);


      //去他想去的页面
      this.$router.push(this.$route.query.redirect);
    },
    logout() {
     
     
      window.isLogin = false;
      this.$router.push("/");
    },
  },
};
</script>



(四) 路由组件缓存

如果确定数据加载完之后,不会发生明确的变化。或者某种固定的条件下才会变化。其实没有必要让它频繁的去加载,浪费资源。

主要用于保留组件状态或避免重新渲染。

利用 keepalive做组件缓存,保留组件状态,提高执行效率。

app.vue
<keep-alive  include="admin">
	<router-view/>
</keep-alive>

include="admin"  这例子中,要活着的组件只有admin
include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max - 数字。最多可以缓存多少组件实例。

在这里插入图片描述


在这里插入图片描述

在这里插入图片描述

使用include 或exclude 时,要给组件设置name

当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。

在这里插入图片描述

Admin.vue
<template>
 ......
</template>
<script>
export default {
     
     
  name:'admin',
  ......
  activated(){
     
     
    console.log("activated");
  },
  deactivated(){
     
     
    console.log("deactivated");
  }
};
</script>

(五) 路由懒加载

路由懒加载能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

 () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
 () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
 () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

猜你喜欢

转载自blog.csdn.net/weixin_42580704/article/details/110743019
今日推荐