zoukankan      html  css  js  c++  java
  • vue之vue-router

    Vue-router(vue 路由)

    官方文档: https://router.vuejs.org/zh/

    一、认识路由

    1.1 什么是路由?

    • 路由是网络工程里的专业术语
    • 路由(routing) 就是通过互联的网络把信息从源地址传输到目的地址的活动
    • 再来讲讲路由器?
      • 路由器提供两种机制:路由和传送
        • 路由是决定数据包从来源到目的地的路径
        • 传送石将输入端的数据转移到合适的输出端
    • 路由中有一个非常重要的概念叫:路由表
      • 路由表本质上就是一个映射表,决定了数据包的指向

    补充:什么是前端渲染,什么是后端渲染?

    1.2 后端路由

    • 网页发展的第一个阶段都是后端渲染。早期的网站开发整个HTML 页面是有服务器渲染的
    • 服务器直接生产渲染好的对应的HTML页面,返回给客户端进行展示
    • 但是一个网站,这么多页面服务器如何处理呢?
      • 一个页面有自己对应的网址,也就是URL
      • URL 会发送到服务器,服务器会通过正则对该URL进行匹配,并且最后交给一个Controller 进行处理
      • Controller 进行给中处理,最后生成HTML或者数据,返回给前端
      • 这就是完成了一个IO操作,这就是一个后端路由
    • 以上这种操作就是后端路由
      • 当我们页面中需要请求不同路径内容时,交给服务器来进行处理,服务器渲染好整个页面,并且将页面返回给客户端
      • 这种情况下渲染号的页面,不需要单独加载任何js和css,可以直接交给浏览器展示。这样也有利于SEO的优化。
    • 后端路由的缺点:
      • 一种情况是整个页面的模块有后端人员来编写和维护
      • 另一种请你赶快是前段开发人员如果想要开发页面,需要通过PHP和java等语言来编写页面代码
      • 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起,编写和维护都非常糟糕的事情

    1.3 前后端分离

    即后端只负责提供数据,不负责任何渲染的内容,渲染的任务交由前端渲染。

    1.3.1前端渲染:

    • 浏览器中显示的网页中的大部分内容,都是由前端写js代码在浏览器中执行,最终渲染出来的网页。

    • 随着Ajax 的出现,有了前端分离的开发模式。

    • 后端只提供API 来返回数据,前端通过ajax 获取数据,并且可以通过javascript 将数据渲染到页面中

    • 这样做最大的优点就是前后端责任的清晰,后端专属于数据上,前端专注于交互和可视化

    • 并且当移动端(ios/android)出现后,后端不需要进行任何处理,依然使用之前额一套API即可。

    • 目前很多的网站依然采用这种开发模式

    1.4 前端路由(单页面富应用阶段):

    1.4.1 是什么?

    • 其实SPA (simple page web application)最主要的特点就是在前后端分离的基础上加上了一层前端路由
    • 也就是前端来维护一套路由规则,整个页面只有一个html页面
    • 对于vue 来说就是一个路由对应一个组件

    1.4.2 实现的效果

    • 改变URL,但是页面不进行整体刷新
    • 如何实现呢?

    1.4.3 URL 的hash

    // hash 方式修改路由
    location.hash = 'aaa'
    
    // history 对象进行修改路由,底层类似堆栈的压栈。
    history.pushState({},'','home')
    
    // 以上两种方式不会刷新页面,但是会改变前段路由
    
    // replace 是替换,不能返回
    history.repalceState({},'','test')
    // back 返回
    history.back()
    // go
    history.pushState({},'','qzk')
    // 弹出一个,相当于 history.back()
    history.go(-1)
    

    1.5 认识vue-router

    • 目前前段的三大主流框架,以及其路由

      • Angular 的ngRouter
      • React 的 ReactRouter
      • Vue 的 vue-router
    • vue-router 是Vue.js 官方的路由插件,他和vue.js 是深度集成的,适合用于构建单页面应用

    • 我们可以访问其官方网站对其进行学习:https://router.vuejs.org/zh/

    • vue-router 是基本的路由和组件的

      • 路由用于设定访问路径,将路径和组件映射起来
      • 在vue-router 的单页面应用中,页面的路径的改变就是组件的切换

    1.6 安装和使用vue-router

    步骤一、安装vue-router

    npm install vue-router --save  # 使用 --save 应该这个vue在运行在客户端的时候也要使用
    
    // project/src/router/index.js
    // 配置路由相关的信息
    // 导入Vue
    import Vue from 'vue'
    // 导入路由
    import Router from 'vue-router'
    import HelloWorld from '@/components/HelloWorld'
    // 使用步骤
    // 第一步:通过 Vue.use() , 安装插件
    // Vue.use(Router)
    
    // 第二步: 创建路由对象
    // const routes = [
    //
    // ]
    // const router = new VueRouter({
    //   routes
    // })
    // 第三部: 将 router 传入 vue实例中
    // 导入 router 对象
    // export  default router
    // 最终就是下面的代码
    export default new Router({
      // 通过 routes 配置映射关系
      routes: [
        {
          path: '/',
          name: 'HelloWorld',
          component: HelloWorld
        }
      ]
    })
    
    
    // main.js
    import Vue from 'vue'
    import App from './App'
    import router from './router'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      render: h => h(App)
    })
    
    

    总结:

    • 第一步:导入路由对象,并且调用Vue.use(VueRouter)
    • 第二步:创建路由实例,并且传入路由映射配置
    • 第三步:在vue实例中挂载创建的路由实例

    二、vue-router 基本实用

    • 创建路由组件
    • 配置路由映射关系:组件和路由的映射关系
    • 会用路由,通过 router-link 和 router-view
    • 在前面的 router-link 标签中,我们知识使用了一个属性:to,用于指定跳转的路径
    • router-link 还有一些其他的属性:
      • tag 属性:指定 router-link 之后渲染成什么组件
      • replace 属性:replace 属性不会留下history 记录,所以指定replace的情况下,后退键返回不能返回到上一个页面
      • active-class 属性:当 router-link 对应的路由匹配成功时,会自动给当前元素设置一个router-link-active 的 class,设置active-class 可以修改默认属性的名称
        • 在进行高亮显示的导航菜单或者底部tabbar时,会使用到该类
        • 到时通常不会修改类的属性,会直接使用默认的router-link-active即可
    <!-- 渲染成btn -->
    <router-link to='/home' tag='button' replace active-class='active'></router-link>
    

    也可以通过其他方式渲染

    <template>
      <div id="app">
        <h1>我是APP哈哈</h1>
    <!--    <router-link to="/home" tag="button">首页</router-link>-->
    <!--    <router-link to="/about">关于</router-link>-->
        <button @click="homeClick">首页</button>
        <button @click="aboutClick">关于</button>
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: 'App',
      methods:{
        homeClick(){
          // $.router 的push 方法 相当于 pushState,是可以返回的
          // this.$router.push('/home')
          // replace 是不可以返回的
          this.$router.replace('/home')
        },
        aboutClick(){
          // this.$router.push('/about')
          this.$router.replace('/about')
        }
      }
    }
    </script>
    
    <style>
    </style>
    
    

    2.3 动态路由

    • 在某些情况下,一个页面的path路径可能是不确定的,比如我们进入用户界面时,希望如下路径:
      • /user/aaa 或者 /user/bbb
      • 除了有前面的/user 一样外,还有跟上用户id等信息
      • 这种path 和component的匹配关系,我们称之为动态路由(也就是路由传递数据的一种方式)
    <!--user.vue 文件-->
    <template>
      <div>
        <h2>用户页</h2>
        <p>用户个人信息</p>
        <!--方式一,通过计算属性获取-->
        <h3>{{userId}}</h3>
        <!--方式二,直接通过模板以及 $route.params 获取-->
        <h3>{{$route.params.userId}}</h3>
    
      </div>
    </template>
    
    <script>
      export default {
        name: "User",
        computed:{
          userId(){
            // 在vue 中 this.$router 指向的是vue router实例
            // this.$route 是指向处于当前活跃状态路由
            return this.$route.params.userId
          }
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    
    // router/index.js
    // 配置路由相关的信息
    // 导入Vue
    import Vue from 'vue'
    // 导入路由
    import Router from 'vue-router'
    // import HelloWorld from '@/components/HelloWorld'
    // 导入组件
    import Home from "../components/Home";
    import About from "../components/About";
    import User from "../components/User";
    // 使用步骤
    // 第一步:通过 Vue.use() , 安装插件
    Vue.use(Router)
    
    // 第二步: 创建路由对象
    const routes = [
      {
        path:'',
        redirect:'/home',
      },
      {
        path: '/home',
        component: Home
      },
      {
        path: '/about',
        component: About
      },
      {
        // 动态路由
        path:'/user/:userId',
        component:User
      }
    ]
    const router = new Router({
      mode:'history',  // html5 的 history 模式
      routes
    })
    // 第三部: 将 router 传入 vue实例中
    // 导入 router 对象
    export default router
    
    
    // 第四部:main.js 中使用router
    import Vue from 'vue'
    import App from './App'
    import router from './router'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      // 使用router
      router,
      render: h => h(App)
    })
    
    

    三、vue-router 的懒加载:

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

    路由懒加载的方式:

    • 方式一:结合Vue的异步组件和webpack的代码分析
    const Home = resolve => {require.ensure(['../componenets/Home.vue'],() => {
      resolve(requir('../components/Home.vue'))})};
    
    • 方式二:AMD写法
    const About = resolve => require(['../components/About.vue'],resolve);
    
    • 方式三:在ES6中,我们可以更加简单的写法来组织Vue异步组件和Webpack的代码分割。
    const Home = () => import('../components/Home.vue')
    

    实现demo中的index,js

    const Home = () => import(../components/Home)
    Vue.use(VueRouter)
    const routes =[
      {
        path:'',
        redirect:'/home',
      },
      {
        path:'/home',
        //component:() => import('../components/Home')
        component:Home
      }
    ]
    

    注:一个路由懒加载,对应一个打包的js文件

    四、vue-router 嵌套路由(实际开发中,路由嵌套是一个很常见的功能)

    • 比如在Home页面中,我们希望通过/home/news 和 /home/message访问一些内容
    • 一个路径映射一个组件,访问这两个路径也会分别渲染两个组件
    • 实现步骤:
      • 创建对应的子组件,并且在路由映射中配置对应的子路由
      • 在组件内部使用 router-view 标签
    // 懒加载home父组件
    const Home = () => import(../components/Home)
    // 懒加载 home news 子组件
    const News = () => import(../components/News)
    Vue.use(VueRouter)
    const routes =[
      {
        path:'',
        redirect:'/home',
      },
      {
        path:'/home',
        //component:() => import('../components/Home')
        component:Home,
        children:[
          // 基于重定向实现默认显示
          {
            path:'',
            redirect:'news'
          },
          {
            path:'news',
            component:News
          }
        ]
      }
    ]
    

    注: 子路由是显示在父路由中的,所以我们还要在其父组件中,所以还要在父组件的vue文件中使用router-link&&router-view

    五、vue-router 参数传递

    vue-router 路由传参主要有两种类型:

    • params
      • 配置路由格式: /router/:id
      • 传递方式:在path(url)后跟上对应的值
      • 传递后形成的路径:/router/123,/router/abc
    • query
      • 配置路由路由格式:/router,也就是普通配置
      • 传递的方式:对象中使用 query的key作为传递方式
      • 传递后形成的路径:/router?id=123 , /router?id=abc

    Demo example

    App.vue

    <template>
      <div id="app">
        <h1>我是APP哈哈</h1>
        <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>-->
        <button @click="userClick">我的</button>
        <router-link to="/profile" tag="button">档案</router-link>
    
        <!--    <router-link :to="{path:'profileQuery',query:{'age':18,'name':'qzk'}}" tag="button">档案传参</router-link>-->
        <button @click="profileClick">档案传参</button>
        <!--    <button @click="homeClick">首页</button>-->
    
        <!--    <button @click="aboutClick">关于</button>-->
        <router-view></router-view>
      </div>
    </template>
    
    <script>
      export default {
        name: 'App',
        data() {
          return {
            userId: 'qzk',
            age: 18,
            heigth: 180
          }
        },
        methods: {
          homeClick() {
            // $.router 的push 方法 相当于 pushState,是可以返回的
            // this.$router.push('/home')
            // replace 是不可以返回的
            this.$router.replace('/home')
          },
          aboutClick() {
            // this.$router.push('/about')
            this.$router.replace('/about')
          },
          userClick() {
            this.$router.push('/user/' + this.userId)
          },
          profileClick() {
            this.$router.push({path: 'profileQuery', query: {"age": 18, "name": "qzk"}})
          }
    
        }
      }
    </script>
    
    <style>
    </style>
    
    

    Router/index.js

    // 配置路由相关的信息
    // 导入Vue
    import Vue from 'vue'
    // 导入路由
    import Router from 'vue-router'
    // import HelloWorld from '@/components/HelloWorld'
    // 导入组件
    // 懒加载方式导入
    const Home = () => import ("../components/Home")
    const About = () => import('../components/About')
    const User = () => import('../components/User')
    const Profile = () => import('../components/Profile')
    const ProfileQuery = () => import('../components/ProfileQuery')
    // const UserInfo = () => import('../components/')
    // import About from "../components/About";
    // import User from "../components/User";
    // import UserInfo from "../components/User";
    // import login from "../components/login/login";
    // 使用步骤
    // 第一步:通过 Vue.use() , 安装插件
    Vue.use(Router)
    
    // 第二步: 创建路由对象
    const routes = [
      {
        path: '',
        redirect: '/home',
      },
      {
        path: '/home',
        component: Home
      },
      {
        path: '/about',
        component: About
      },
      {
        path: '/user/:userId',
        component: User
      },
      // {
      //   path: '/userinfo',
      //   component: UserInfo
      // },
      {
        path: '/profile',
        component: Profile
      },
      {
        path:'/profileQuery',
        component:ProfileQuery
      }
    ]
    const router = new Router({
      mode: 'history',  // html5 的 history 模式
      routes
    })
    // 第三部: 将 router 传入 vue实例中
    // 导入 router 对象
    export default router
    // export default new Router({
    //   // 通过 routes 配置映射关系
    //   routes: [
    //     {
    //       path:'/home',
    //       component: Home
    //     },
    //     {
    //       path:'/about',
    //       component:About
    //     }
    //   ]
    // })
    
    

    相关组件

    About.vue

    <template>
    <div>
      <h1>我是关于</h1>
      <p>我是关于,哈哈哈</p>
    </div>
    </template>
    
    <script>
      export default {
        name: "About"
      }
    </script>
    
    <style scoped>
    
    </style>
    
    

    Home.vue

    <template>
      <div>
        <h1>我是首页</h1>
        <p>我是首页内容 ,哈哈哈</p>
      </div>
    </template>
    
    <script>
      export default {
        name: "Home"
      }
    </script>
    
    <style scoped>
    
    </style>
    
    

    Profile.vue

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

    ProfileQuery.vue

    <template>
      <div>
        <h2>我是ProfileQuery 组件</h2>
        <h3>{{$route.query}}</h3>
        <h3>{{$route.query.name}}</h3>
        <h3>{{$route.query.age}}</h3>
      </div>
    </template>
    
    <script>
      export default {
        name: "Profile"
      }
    </script>
    
    <style scoped>
    
    </style>
    
    

    User.vue

    <template>
      <div>
        <h2>用户页</h2>
        <p>用户个人信息</p>
        <h3>{{userId}}</h3>
        <h3>{{$route.params.userId}}</h3>
      </div>
    </template>
    
    <script>
      export default {
        name: "User",
        computed:{
          userId(){
            // 在vue 中 this.$router 指向的是vue router实例
            // this.$route 是指向处于当前活跃状态路由
            return this.$route.params.userId
          }
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    
    

    Main.js

    import Vue from 'vue'
    import App from './App'
    import router from './router'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      // 使用router
      router,
      render: h => h(App)
    })
    
    

    六、vue中$route 和 $router 的区别

    • $router 为VueRouter 实例,想要导航到不同的URL,则使用$router.push
    • $route 为当前router跳转对象里面可以获取name,path,query,params等
    • $router 是vue实例传入的router对象
    • $route 是当前活跃的route对象

    七、vue-router 导航守卫

    对前段路由来回跳转的过程的一个监听,即监听从哪里跳转到哪里

    需求:跳转同事导航条上的title变更

    我们在来分析一下需求:(如何改变网页的标题)

    • 网页标题是通过title 标签来显示的,但是SPA 只有一个固定的HTML ,切换不同的页面时,标题并不会改变
    • 但是我们可以通过js来修改title的内容,window.document.title = "新标题"
    • 那么在vue项目中,在哪里修改?什么时候修改比较合适:
      • 方式一:在组件的生命周期函数中修改mounted
        • 我们比较绒被冠以想到的修改标题的位置是每一个路由对应的组件.vue文件中
        • 通过mounted 生命周期函数,执行对应的代码进行修改
        • 但是当页面比较多的时候,这种方法不易维护(因为要在多个页面执行类似的代码)
      • 方式二:通过导航守卫,监听页面跳转,然后进行修改

    什么是导航守卫?

    • vue-router 提供的导航守卫主要用来监听路由的进入和离开
    • vue-router 提供的beforeEach 和 afterEach 的钩子函数,踢门会在路由即将改变前和改变后触发

    /router/index.js

    // 配置路由相关的信息
    // 导入Vue
    import Vue from 'vue'
    // 导入路由
    import Router from 'vue-router'
    // import HelloWorld from '@/components/HelloWorld'
    // 导入组件
    // 懒加载方式导入
    const Home = () => import ("../components/Home")
    const About = () => import('../components/About')
    const User = () => import('../components/User')
    const Profile = () => import('../components/Profile')
    const ProfileQuery = () => import('../components/ProfileQuery')
    // const UserInfo = () => import('../components/')
    // import About from "../components/About";
    // import User from "../components/User";
    // import UserInfo from "../components/User";
    // import login from "../components/login/login";
    // 使用步骤
    // 第一步:通过 Vue.use() , 安装插件
    Vue.use(Router)
    
    // 第二步: 创建路由对象
    const routes = [
      {
        path: '',
        redirect: '/home',
      },
      {
        path: '/home',
        component: Home,
        meta:{
          title:'首页',
        }
      },
      {
        path: '/about',
        component: About,
        meta:{
          title:'关于',
        }
      },
      {
        path: '/user/:userId',
        component: User,
        meta:{
          title:'用户',
        },
      },
      // {
      //   path: '/userinfo',
      //   component: UserInfo
      // },
      {
        path: '/profile',
        component: Profile,
        meta:{
          title:'档案',
        }
      },
      {
        path:'/profileQuery',
        component:ProfileQuery,
        meta:{
          title:'档案2'
    
        },
      }
    ]
    const router = new Router({
      mode: 'history',  // html5 的 history 模式
      routes
    })
    // 第三部: 将 router 传入 vue实例中
    // 导入 router 对象
    router.beforeEach((to,from,next) =>{
      // 从form 跳转到to
      document.title = to.matched[0].meta.title
      next()
    })
    export default router
    // export default new Router({
    //   // 通过 routes 配置映射关系
    //   routes: [
    //     {
    //       path:'/home',
    //       component: Home
    //     },
    //     {
    //       path:'/about',
    //       component:About
    //     }
    //   ]
    // })
    
    

    导航守卫补充

    • 补充一:如果是后置守卫,也就是afterEach, 不需要主动调用next() 函数
    • 补充二:上面我们使用的是导航守卫,被称之为全局守卫
      • 路由独享的守卫
    const router = new VueRouter({
      routes: [
        {
          path: '/foo',
          component: Foo,
          beforeEnter: (to, from, next) => {
            // ...
          }
        }
      ]
    })
    
    • 组件内的守卫
    const Foo = {
      template: `...`,
      beforeRouteEnter (to, from, next) {
        // 在渲染该组件的对应路由被 confirm 前调用
        // 不!能!获取组件实例 `this`
        // 因为当守卫执行前,组件实例还没被创建
      },
      beforeRouteUpdate (to, from, next) {
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
        // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
        // 可以访问组件实例 `this`
      },
      beforeRouteLeave (to, from, next) {
        // 导航离开该组件的对应路由时调用
        // 可以访问组件实例 `this`
      }
    }
    

    demo

    Bad demo

    // BAD
    router.beforeEach((to, from, next) => {
      if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
      // 如果用户未能验证身份,则 `next` 会被调用两次
      next()
    })
    

    Good Demo

    // GOOD
    router.beforeEach((to, from, next) => {
      if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
      else next()
    })
    

    八、keep-alive 和 vue-router(切换路由时,切换回来的时候保留原先的状态信息)

    • keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
      • Keep-alive 有两个非常重要的属性:
        • include — 字符串或正则表达,只有匹配的组件才会被缓存
        • exclude — 字符串或正则表达式,任何匹配的组件都不会被缓存
    • router-view 也是一个组件,如果直接被包在keep-alive 里面,所有的路由匹配到的视图组件都会被缓存
    <template>
      <div>
        <h1>我是首页</h1>
        <p>我是首页内容 ,哈哈哈</p>
        <router-link :to="{name:'news'}" tag="button">新闻</router-link>
        <router-link :to="{name:'msgs'}" tag="button">档案</router-link>
        <keep-alive exclude='Profile,user'>
          <router-view></router-view>
        </keep-alive>
    <!--    <router-view></router-view>-->
      </div>
    </template>
    
    <script>
      export default {
        name: "Home",
        data(){
          return {
            message:'你好',
            path:'/home/news'
          }
        },
        // 组件Vue实例被创建的时候调用
        created() {
          console.log('home created')
          // this.$router.push(this.path)
        },
        mounted() {
          console.log('home mounted')
        },
        // 组件实例被销毁的时候调用
        destroyed() {
          console.log('home destory')
        },
        // activated 和 deactivated 钩子函数只有在该组件被保持了状态使用了 keep-alive 时 才有效
        // 该函数只有在有keep-alive 时 钩子才生效
        activated() {
          console.log('home activated')
          this.$router.push(this.path)
        },
        //该函数只有在有keep-alive 时 钩子才生效
        deactivated() {
          console.log('home deactivated')
        },
        // 路由守卫
        beforeRouteLeave(to,from,next){
          console.log(this.$route.path)
          this.path = this.$route.path
          next()
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    
  • 相关阅读:
    BZOJ2938 POI2000病毒
    HDU2222 AC自动机模板
    BZOJ1029 [JSOI2007]建筑抢修
    BZOJ1027 [JSOI2007]合金
    BZOJ1025 [SCOI2009]游戏
    BZOJ1257 [CQOI2007]余数之和sum
    BZOJ1021 SHOI2008循环的债务
    BZOJ1030 [JSOI2007]文本生成器(AC自动机)
    POJ2728 Desert King
    POJ2976 Dropping tests
  • 原文地址:https://www.cnblogs.com/qianzhengkai/p/13227328.html
Copyright © 2011-2022 走看看