vue-router基本使用、路由传参、懒加载、嵌套路由、导航守卫、keep-alive

vue入门–基础命令+axios+案例练习
vue入门–vue常用属性、生命周期、计算属性、过滤器、组件、虚拟DOM、数组的响应式方法、页面闪烁、ES6简单语法增强
vue入门–js高阶函数(箭头函数)、v-model数据绑定、组件化、父子组件通信及访问
vue入门–插槽(具名、匿名、作用域插槽)+ES6模块化导入导出+webpack的使用(基本使用+配置使用+如何一步步演化成cli脚手架)+webpack插件使用(搭建本地服务器、配置文件分离)
vue-cli脚手架2版本及3+版本安装、目录解析、only和compiler的区别、3+版本如何改配置、箭头函数及this的指向
vue-router基本使用、路由传参、懒加载、嵌套路由、导航守卫、keep-alive
Promise基本使用、三种状态、链式调用及简写、all方法
Vuex的作用、使用、核心概念(State、Mutations、Getters、Actions、Modules)、文件抽离

vue路由 vue-router

路由有一个非常重要的概念叫路由表,路由表本质上就是一个映射表,决定了数据包的指向,可以进行路由和转发。

前后端分离:

后端只负责提供数据,不负责任何界面的内容。通常情况下,我们会把前端打包后的资源放置到一个静态资源服务器上,如Nginx。然后还会有一个后端服务器为客户端提供API接口。

我们只需要使用浏览器访问静态资源服务器,然后从静态服务器上拉取到HTML+CSS+JS,浏览器再去执行js代码,一般会去访问后端服务器提供的API接口,返回数据给客户端(浏览器)后再进行页面数据的渲染。

前端渲染,后端渲染、前端路由、后端路由:

  • 前端渲染:浏览器中显示的页面中的大部分内容,都是由前端写的js代码在浏览器(客户端)中执行,最后渲染出来的网页页面。
  • 前端路由:SPA页面(单页面富应用),在前后端分离阶段中,增加了一层前端路由,html只有一个,通过不同的路由,将不同的组件展示到一个html中,整个应用中只有一个html页面。通过映射关系实现。前端来管理URL和页面之间的映射关系。
  • 后端渲染:也叫作服务器端渲染,之前会采用jsp技术,当请求发送到服务器(后端),由服务器进行查询数据,并且通过jsp技术将页面数据渲染好,直接返回给浏览器一个 HTML+CSS,因为是完全后端渲染,还未用到js、ajax等
  • 后端路由:后端服务器来处理URL和页面之间的映射关系。

url的hash和HTML5的history

既然说到了前端路由,当我浏览器更改url时,如何不让浏览器再去访问静态资源服务器呢,可以使用url的hash或html5的history。

  • url的hash:url的hash也就是锚点(#),本质上是改变window.location.href的属性。可以通过location.hash改变href,但是页面不会发生刷新,且url中多了一个#锚点

  • html5的history:通过history的pushState也可以做到

    // 入栈,入栈后浏览器会记录历史,可操作上一步,下一步
    history.pushState({},'','home')
    
    // 返回
    history.back()
    
    // 替换,不会记录历史
    history.replaceState({}, '', 'demo')
    

基本使用

首先安装 vue-router

npm install vue-router --save

然后再模块化工程中使用它,由于vue-router是一个插件,所以可以通过Vue.use()来使用

创建组件 Home.vue

<template>
    <div>
        <h2>首页</h2>
    </div>
</template>

<script>
export default {
    name: "Home"
}
</script>
<style>
</style>

创建组件 About.vue

<template>
    <div>
        <h2>关于</h2>
    </div>
</template>

<script>
export default {
    name: "About"
}
</script>
<style>
</style>

在src目录中有一个router的目录,然后创建一个index.js文件,内容如下:

// 配置路由相关的信息
import Vue from 'vue'
import VueRouter from 'vue-router'

// 导入组件
import Home from '../components/Home'
import About from '../components/About'

// 1.通过Vue.use(VueRouter),使用这个插件
Vue.use(VueRouter)

// 2.创建路由对象
const routes = [
    {
        path: "/home",
        component: Home
    },
    {
        path: "/about",
        component: About
    }
]
const router = new VueRouter({
    routes
});

//3.将router传给vue实例
export default router

在src目录中的main.js入口文件中,创建vue实例并且使用路由对象。

import Vue from 'vue'
import App from './App'
import router from './router'	// 当from文件夹时,它会自动找文件夹下的index.js入口文件

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

在App.vue入口组件中使用这些

router-link和router-view配合使用

<template>
  <div id="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}

路由默认路径

const routes = [
    {
        // 匹配到 / 时(默认值),直接重定向到 /home路由
        path: "/",	
        redirect: '/home'
    }
]

路由改为history模式(去掉#锚点)

创建vue-router实例时,指定mode为history

const router = new VueRouter({
    routes,
    mode: 'history'
});

router-link属性

  • to : 点击时router-view中替换为相应路由下的组件
  • tag : router-link渲染到html中会被渲染为 a标签,可以根据tag属性更改为其他标签,如div,button
<router-link to="/home" tag="button">首页router-link>
  • replace ,增加此属性后,跳页面底层用的是 replaceState(),不会存在历史记录,不能点左右箭头

  • active-class : 当router-link处于激活状态时(被点击),增加一个类属性(不写时默认为 router-link-active)

<router-link to="/home" tag="button" active-class="active">首页router-link>
<router-link to="/about" active-class="active">关于router-link>

<style>
  .active{
    color: red;
  }
style>

实现后,点击哪个 router-link,哪个router-link中的文本就会变成红色的

但是假如有多个 router-link,难道一个一个加 active-class属性吗,当然不用。我们可以在实例化路由时进行全局化配置:

const router = new VueRouter({
    routes,
    mode: 'history',
    linkActiveClass: 'active'
});

通过代码跳转路由

当我们不使用 router-link进行路由跳转时,还可以通过代码进行路由跳转。

<template>
  <div id="app">
    <!-- <router-link to="/home" tag="button" >首页</router-link>
    <router-link to="/about" >关于</router-link> -->
    <button @click="homeClick">首页</button>
    <button @click="aboutClick">关于</button>
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
     homeClick(){
         // 从 data()中默认返回的一个 $router对象
       this.$router.push('/home')	// 有历史记录
       //this.$router.replace('/home')	// 直接替换,无历史记录
       console.log('homeClick')
     },
     aboutClick(){
       this.$router.push('/about')
       console.log('aboutClick')
     }
  },
  // 注意,在vue中,所有的vue组件都会默认创建一个$router的属性,这里不需要写,是默认的,只是为了说明			$router是从哪里来的
  data(){
      return {
          $router : ...
      }
  }
}
</script>

那么这种方式如果连续点击几次后发现报错了,那么可以给push或replace函数加一个异常捕获,我们直接把异常给吃掉。

this.$router.push('/home').catch(err => {})
// 或
this.$router.replace('/home').catch(err => {})

动态路由

如访问到某一个用户id为2的详细信息,路径就是变成 /user/2

配置路由时,在path上面加传值匹配,匹配路由时 /user/xxx才会被匹配到,否则无法匹配到就找不到路由

const routes = [
   	......
    {
        path: "/user/:userId",
        component: User
    }
]

使用时(App.vue文件中),将具体的值拼接在path上面即可

用户

但是实际使用时那个id是需要从后端服务器动态获取来的,假如这么一个userId变量为2

export default {
  name: 'App',
  data() {
    return {
      userId: 2
    }
  }
}

那么如何拼接上这个变量呢?需要给router-link绑定上to属性

<router-link :to="'/user/' + userId">用户</router-link>
<router-view/>

实际项目中,我们需要从url中拿到我们path上面拼接的userId,并且在组件中做一个展示,那么我们如何做呢?

User.vue

<template>
    <div>
        <h2>用户界面</h2>
        <h2>{{userId}}</h2>
        <h2>{{$route.params.userId}}</h2> <!--{{userId}}一样的效果 -->
    </div>
</template>

<script>
export default {
    name: "User",
    // 我们可以创建一个计算属性,里面去 $route中获取参数列表中的 userId变量(path中拼接的那个值),参数列		表中的 变量名都是在 路由配置时定义的
    computed:{
        userId(){
            return this.$route.params.userId;
        }
    }
}
</script>

那么前面我们学到的 $router,这里又来了一个 $route,这两个是什么意思呢?

$router:是获取到的我们创建的 vue-router的实例

const router = new VueRouter({
    routes,
    mode: 'history'
});

r o u t e : 是 我 们 获 取 到 的 处 于 活 跃 状 态 的 一 个 路 由 , 比 如 在 A p p . v u e 中 , 显 示 的 是 U s e r 组 件 , 那 么 这 个 route:是我们获取到的处于活跃状态的一个路由,比如在App.vue中,显示的是 User组件,那么这个 routeApp.vueUserroute就是获取的 User组件相关的路由。

const routes = [
   	......
    // User组件的相关路由
    {
        path: "/user/:userId", //:userId,是在参数列表中定义了一个 userId的变量,组件中可以取出来
        component: User
    }
]

vue-router打包文件解析

我们先执行 npm run build 打包一下项目,然后打开dist目录下的js目录,会发现:

vue-router基本使用、路由传参、懒加载、嵌套路由、导航守卫、keep-alive_第1张图片

路由懒加载

当打包构建应用时,JavaScript包会变得非常大,影响页面的加载。如果我们能把不同路由对象的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样会很高效。

由上面打包解析可知,为我们模块导入导出做支撑的代码和第三方插件代码一定要先加载,不然会导致功能不可用。那么我们可以懒加载入手的就是我们的业务层代码,需要用到时再加载某个业务代码。

如何使用?

在配置路由的时候,不直接配置组件,而是配置一个箭头函数,然后导入相关的组件,这样子就能够每一个路由生成一个js文件,当浏览器访问时,不会把所有的资源js文件都下载到本地,而是用到某一个路由的时候,再把相应的路由js文件下载到浏览器执行。

  • 方式一:AMD写法

    const About = resolve => require(['../components/Abount.vue'],resolve)
    
  • 方式二:ES6写法(推荐)

    const Home = () => import('../components/Home.vue')
    

详细router下的index.js配置

// 配置路由相关的信息
import Vue from 'vue'
import VueRouter from 'vue-router'

// 路由懒加载方式
const Home = () =>  import('../components/Home')
const About = () =>  import('../components/About')
const User = () =>  import('../components/User')

Vue.use(VueRouter)
// 2.创建路由对象
const routes = [
    {
        path: "/",
        redirect: '/home'
    },
    {
        path: "/home",
        component: Home
    },
    {
        path: "/about",
        component: About
    },
    {
        path: "/user/:userId",
        component: User
    }
]
const router = new VueRouter({
    routes,
    mode: 'history'
});

//3.将router传给vue实例
export default router

然后执行打包npm run build,打包后的js目录中,可以看到一个懒加载路由对应一个js文件。

vue-router基本使用、路由传参、懒加载、嵌套路由、导航守卫、keep-alive_第2张图片

嵌套路由

假如Home主页组件中又有两个子组件要分别展示,那么就需要用到嵌套路由。实现嵌套路由有两个步骤:

  • 创建对应的子组件,并且在路由映射配置对应的子路由
  • 在组件内部使用标签

定义两个组件

HomeNews.vue

<template>
    <div>
        <ul>
            <li>新闻1</li>
            <li>新闻2</li>
            <li>新闻3</li>
        </ul>
    </div>
</template>

<script>
export default {
    name: "HomeNews"
}
</script>

HomeMessage.vue

<template>
    <div>
        <ul>
            <li>消息1</li>
            <li>消息2</li>
            <li>消息3</li>
        </ul>
    </div>
</template>

<script>
export default {
    name: "HomeMessage"
}
</script>

在Home路由中配置子路由,HomeNews和HomeMessage

// 采用懒加载路由方式
const Home = () =>  import('../components/Home')
const HomeNews = () =>  import('../components/HomeNews')
const HomeMessage = () =>  import('../components/HomeMessage')

const routes = [
    {
        path: "/",
        redirect: '/home'
    },
    {
        path: "/home",
        component: Home,
        children:[	// 在Home路由下,配置子路由
            {
                path: 'news',	// 子路由中的path 不能加 /,直接定义path就行
                component: HomeNews
            },
            {
                path: 'message',
                component: HomeMessage
            }
        ]
    },
]

在Home.vue中使用标签,展示子组件

<template>
    <div>
        <h2>首页</h2>
        <!-- 这里 to路径一定要写全,否则找不到 -->
        <router-link to="/home/news">新闻</router-link>
        <router-link to="/home/message">消息</router-link>
        <router-view></router-view>
    </div>
</template>

同样可以在子组件中定义一个默认路由:

children:[
    {
        // path直接为空即可
        path: '',
        // 重定向到 news
        redirect: 'news'
    },
    {
        path: 'news',
        component: HomeNews
    },
    {
        path: 'message',
        component: HomeMessage
    }
]

参数传递

传递参数主要有两种类型:params和query

params方式

配置动态路由的时候,在path上拼接对应的值,然后在组件中可以this.$route.params.xxx获取到这个值。

query方式

普通方式配置路由,传递时使用对象的方式来进行传值,路径上会变成问号传值的方式把数据展示在url上面。

比如配置一个Profile.vue组件:

<template>
    <div>
        <h2>我是Profile组件</h2>
    </div>
</template>
<script>
export default {
    name: "Profile"
}
</script>

配置路由:

{
    path: "/profile",
    component: Profile
}

具体传值方式:直接绑定 to属性,给传入一个对象。path就是跳转路径,query中写的是拼接的k-v

档案

在Profile.vue中如何取出这些数据呢?

使用 $route.query可以获取到一个对象,$route.query.xxx获取到具体的属性的值

<template>
    <div>
        <h2>我是Profile组件</h2>
        <h2>{{$route.query.name}}</h2>
        <h2>{{$route.query.age}}</h2>
    </div>
</template>

如果不使用router-link,使用代码方式呢?

$router.push方式进行跳转,push中可以直接传递一个对象。

<template>
  <div id="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <button @click="userClick">用户</button>
    <button @click="profileClick">档案</button>
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      userId: 2
    }
  },
  methods: {
    userClick(){
      this.$router.push('/user/'+ this.userId)
    },
    profileClick(){
      this.$router.push({
        path: "/profile",
        query:{
          name: 'wlh',
          age: 21
        }
      })
    } 
  }
}
</script>

导航守卫

监听从某一个组件跳到另一个组件时,我们可以做些事情。比如,从一个组件跳到另一个组件时,把当前的title标题改一下。

再来复习下vue的生命周期

  • created() : 当vue实例创建完之后,会发生回调
  • mounted() : 当template挂载到整个dom后回调
  • update() : 页面发生变化时,回调

那么在vue-router中如何全局监听路由的跳转呢?

前置钩子(监听回调函数) 监听跳转前

前置钩子,或者叫前置守卫。

router实例中有一个 beforeEach()的方法,需要在此方法中进行操作。我们可以在路由配置时给每个route对象加上一些元数据

const routes = [
...
   {
        path: "/about",
        component: About,
        meta:{
            title:"关于"
        }
    },
    {
        path: "/user/:userId",
        component: User,
        meta:{
            title:"用户"
        }
    },
    {
        path: "/profile",
        component: Profile,
        meta:{
            title:"档案"
        }
    }
]
const router = new VueRouter({
    routes,
    mode: 'history'
});

// 使用 router的beforeEach方法 (前置钩子函数)
router.beforeEach((to, from, next) => {
    // 从 to中(要跳转到的route中) 找到元数据,给当前title赋值
    document.title = to.matched[0].meta.title;
    // 放行,注意如果不放行,路由就不会跳转了
    next()
});

to(也就是route对象)中有个matched的列表,这里存放的是所有有关联的route,一般是父子路由信息展示为一个列表,只找第一个(父路由)即可。

后置钩子 (监听跳转后)(后置守卫)

既然有前置钩子,那么同样有后置钩子,感觉跟spring的拦截器很像,方法执行前拦截和方法执行后拦截。。

如果是后置钩子afterEach函数,不需要调用next()函数,因为next()已经执行过了

router.afterEach((to, from) => {
    console.log('--------------')
});

next()

守卫中有个next()函数,这个函数有几种传参方式:

  • next() : 无参,执行下一步跳转到该跳转的路由下
  • next(false) : 直接中断跳转
  • next(‘/login’) 或者next({path:‘/login’}),跳到别的路由(多用于判断登录)

那么上面说的都是全局守卫,能否搞一些局部守卫呢?当然可以

局部守卫

  • 路由独享守卫, beforeEnter函数

    const routes = [
      {
        path: '/users/:id',
        component: UserDetails,
        beforeEnter: (to, from) => {
          // reject the navigation
          return false
        }
      },
    ]
    

    beforeEnter 守卫 只在进入路由时触发,不会在 paramsqueryhash 改变时触发。例如,从 /users/2 进入到 /users/3 或者从 /users/2#info 进入到 /users/2#projects。它们只有在 从一个不同的 路由导航时,才会被触发。

  • 组件内守卫(见名知意,是在组件中创建的)

    • beforeRouteEnter
    • beforeRouteUpdate
    • beforeRouteLeave
    const UserDetails = {
      template: `...`,
      beforeRouteEnter(to, from) {
        // 在渲染该组件的对应路由被验证前调用
        // 不能获取组件实例 `this` !
        // 因为当守卫执行时,组件实例还没被创建!
      },
      beforeRouteUpdate(to, from) {
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
        // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
        // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
      },
      beforeRouteLeave(to, from) {
        // 在导航离开渲染该组件的对应路由时调用
        // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
      },
    }
    

不过,我们可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数:

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

keep-alive

  • keep-alive是vue内置的一个组件,可以使被包含的组件保留状态,避免重新被渲染
  • router-view也是一个组件,如果直接被包在 keep-alive中,所有路径匹配到的试图组件都会被渲染。

实际上,我们前面的路由跳转,每次跳转时,上一次的路由的组件都会被销毁,跳转到一个组件时再创建这个需要跳转到的vue组件。但是实际项目中可能存在需求:要保留我上次组件状态

使用keep-alive标签后,里面的路由跳转时都不会被销毁,而是保存在内存中。

keep-alive使用后,只有被包含在keep-alive中的组件,可以使用 activated()和deactivated(),来监听当前组件是否是活跃状态.需要注意的是:这个被保存的组件如果下面有子组件,那么子组件是不会被保存起来的

App.vue中,使用keep-alive,也就是里面的所有组件都只被创建一次后会被保存到内存中。

<template>
  <div id="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <!-- <router-link :to="'/user/' + userId">用户</router-link>
    <router-link :to="{path:'/profile', query: {name:'wlh', age:21}}">档案</router-link> -->
    <button @click="userClick">用户</button>
    <button @click="profileClick">档案</button>
    <keep-alive>
      <router-view/>
    </keep-alive>
  </div>
</template>

在Home.vue子组件中:

<template>
    <div>
        <h2>首页</h2>
        <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",
    data(){
        return {
            path: '/home/news'
        }
    },
    activated() {
        this.$router.push(this.path)
    },
    // 在导航离开渲染该组件的对应路由时调用
    beforeRouteLeave (to, from, next) {
        // this.path = from.path
        this.path = this.$route.path;
        next()
    }
}
</script>

其实keep-alive中,有两个很重要的属性

  • include : 字符串或者正则表达式,只有匹配的组件才会被缓存
  • exclude : 字符串或者正则表达式,匹配的组件不被缓存
<keep-alive exclude="Profile,User">
    <router-view/>
</keep-alive>
vue入门–基础命令+axios+案例练习
vue入门–vue常用属性、生命周期、计算属性、过滤器、组件、虚拟DOM、数组的响应式方法、页面闪烁、ES6简单语法增强
vue入门–js高阶函数(箭头函数)、v-model数据绑定、组件化、父子组件通信及访问
vue入门–插槽(具名、匿名、作用域插槽)+ES6模块化导入导出+webpack的使用(基本使用+配置使用+如何一步步演化成cli脚手架)+webpack插件使用(搭建本地服务器、配置文件分离)
vue-cli脚手架2版本及3+版本安装、目录解析、only和compiler的区别、3+版本如何改配置、箭头函数及this的指向
vue-router基本使用、路由传参、懒加载、嵌套路由、导航守卫、keep-alive
Promise基本使用、三种状态、链式调用及简写、all方法
Vuex的作用、使用、核心概念(State、Mutations、Getters、Actions、Modules)、文件抽离

你可能感兴趣的:(vue,vue.js,前端,javascript)