大白话理解和初步使用vue-router

TL;DR

  • router其实就是让路径和组件一一对应
  • 即使不使用vue-router也能够实现跳转,但老是手动处理历史记录
  • vue-router的使用好处:历史记录、参数处理、路由钩子、权限控制、记录滚动条位置
  • vue-router指南vue-router的API
  • 坑:历史模式懒加载的时候注意名字、子路由通常不用/

router究竟是什么

router,路由,我理解的其实就是,让不一样的路径对应的不一样的视图。在vue里,其实视图就至关于组件。也就是让不一样的路径对应不一样的组件。换言之,router就是规定了路径和组件一一对应。这句多是理解vue-touter的核心。javascript

先说说render,为下个标题铺垫

vue官网对render解释的很清楚,这边我简单的说下。 通常咱们写vue组件的时候,会写template,但任何template均可以用render函数代替,事实上,vue就是把template转换为render函数的,之因此咱们用template,是由于其易读易写。html

怎么用render函数替换template呢?
简单的举个例子,你的template是<h1 :title="blogTitle">{{ blogTitle }}</h1>,对应的render函数以下vue

render: function (createElement) {
  // `createElement`的参数通常有三个,第一个必填就是标签名或者组件名,第二个是这个标签的属性,第三个就是子节点。
  return createElement('h1', {attrs:{title:this.blogTitle}}, [this.blogTitle])
}
复制代码

createElement其实更准确的名字是createNodeDescription,由于它所包含的信息会告诉 Vue 页面上须要渲染什么样的节点,包括及其子节点的描述信息。也就是“虚拟节点 (virtual node)”,简写为“VNode”。“虚拟 DOM”是对由 Vue 组件树创建起来的整个 VNode 树的称呼。java

即使不用vue-router,也能够实现单页面跳转的

开始总觉得,要想不一样路径就必需要vue-router,其实没必要。vue官方文档有个很好的demo,这边挪用下逻辑。若是你只是须要简单的路由,也是能够直接这么用的。node

// <div id="app"><div>
const Home = { template: "<p>home page</p>" };
const About = { template: "<p>about page</p>" };
const NotFound = { template: "<p>Page not found</p>" };
// 路由 路径和组件一一对应
const routes = {'/':Home,'about':About}
var app = new Vue({
el: "#app",
data: {
    // 拿到地址栏路径
    currentRoute: window.location.pathname
},
computed: {
    // 由routes获得路径对应的组件
    ViewComponent() {
      return routes[this.currentRoute] || NotFound;
    }
},
// 这边用render了
render(h) {
    return h(this.ViewComponent);
}
});
// 历史记录的处理
window.addEventListener('popstate', () => {
  app.currentRoute = window.location.pathname
})
复制代码

用vue-router有什么好处

  • 本身写上面一套繁琐哇
  • 不一样路径很轻易对应不一样组件,并且人家处理好了历史记录
  • 轻易解决传参数问题
  • 有路由钩子,控制什么状况去什么路径,好比不登陆的话就去登陆页面
  • 甚至能够记住滚动条的位置

官网就是很好的学习路径

vue-router官网,如下我简单的总结下。代码示例统一在末尾。webpack

怎么根据路径显示不一样组件的的

  • vue拿到 地址栏的路径(如/user/user-list
  • => 找routes 那边的配置,从上到下找 (如path:'/user'),拿到components,(如找到{default:Foo,a:Bar}
  • => 由于/user就是第一级路径,因此直接将app.vue里面的<router-view/>替换成Foo组件<router-view name="a"/>替换成Bar组件
  • => 再继续,找path:'/user'下面的children,发现目标path:'user-list',拿到components,找到{default:Second,a:Zoo}
  • => 由于user-list是user的children,因此将user.vue里面的<router-view/>替换成Second组件<router-view name="a"/>替换成Zoo组件
  • 找不到的话会报错,固然通常都会配置404

示例代码

<!-- 示例1 -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!-- 使用 router-link 组件来导航. -->
    <!-- 经过传入 `to` 属性指定连接. -->
    <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>
<script> // 0. 若是使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter) // 1. 定义 (路由) 组件。也就是视图!!!! // 能够从其余文件 import 进来 const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' } // 2. 定义路由。也就是什么路径显示什么视图(组件)!!! // 每一个路由应该映射一个组件。 其中"component" 能够是 // 经过 Vue.extend() 建立的组件构造器, // 或者,只是一个组件配置对象。 // 咱们晚点再讨论嵌套路由。 const routes = [ { path: '/foo', component: Foo }, // 懒加载模式,会在合适的时机加载 bar.js,webpackChunkName就是将此组件的代码命名为bar.js 看控制台的network { path: '/bar', component: () => import(/* webpackChunkName: "bar" */ './views/Bar.vue'), }, // 注意 404 { path: '*', component: () => import(/* webpackChunkName: "404" */ './views/404.vue'), }, ] ] // 3. 建立 router 实例,而后传 `routes` 配置。 // 你还能够传别的配置参数, 不过先这么简单着吧。 const router = new VueRouter({ routes // (缩写) 至关于 routes: routes }) // 4. 建立和挂载根实例。!!!挂载在根实例下,也就是任何组件内均可以经过this.$router知道内容 // 记得要经过 router 配置参数注入路由, // 从而让整个应用都有路由功能 const app = new Vue({ router }).$mount('#app') // 如今,应用已经启动了! </script>
复制代码
// 示例1 js
// Home.vue
export default {
  computed: {
    username () {
      // 咱们很快就会看到 `params` 是什么
      return this.$route.params.username
    }
  },
  methods: {
    goBack () {
      window.history.length > 1
        ? this.$router.go(-1)
        : this.$router.push('/')
    }
  }
}
复制代码
/* 示例2:有嵌套路由的话 */

  routes: [
    { path: '/user/:id', component: User,
      children: [
        {
          // 当 /user/:id/profile 匹配成功,
          // UserProfile 会被渲染在 User 的 <router-view> 中
          path: 'profile',
          component: UserProfile
        },
        {
          // 当 /user/:id/posts 匹配成功
          // UserPosts 会被渲染在 User 的 <router-view> 中
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]

/* 示例3:router.push使用 */

// push的两种参数状况,尽可能用路径的形式,用name的话有时候子路由可能不太方便
// -> /user/123
router.push({ path: `/user/${userId}` })
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { userId }})
// replace
router.replace({ path: 'register', query: { userId }})
router.go(1)
router.go(-1)


/* 示例4:router-view多个的状况 */
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>

{
  path: '/',
  components: {
    default: Foo,
    a: Bar,
    b: Baz
  }
}

/* 示例5:重定向和别名 */

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
    // { path: '/b', component: B, alias: '/a' }
  ]
})


/* 示例6:导航守卫 */

const router = new VueRouter({ ... })
// router上面的钩子
router.beforeEach((to, from, next) => {
  next()
//   next(false)
//   next('/')
})

router.afterEach((to, from) => {
  // ...
})

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      // route上的钩子
      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`
  }
}

/* 示例7:meta的使用 */

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})
router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // this route requires auth, check if logged in
    // if not, redirect to login page.
    if (!auth.loggedIn()) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // 确保必定要调用 next()
  }
})

/* 示例8:跳转路径的特效 */

<transition>
  <router-view></router-view>
</transition>

const Foo = {
  template: `
    <transition name="slide">
      <div class="foo">...</div>
    </transition>
  `
}

/* 示例9:根据路径的参数去请求数据 这边显示跳转以后请求数据 */
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
        }
      })
    }
  }
}

/* 示例10:控制滚动条的位置 */
scrollBehavior (to, from, savedPosition) {
  if (savedPosition) {
    return savedPosition
  } else {
    return { x: 0, y: 0 }
  }
}
复制代码

写例子来实践

这里直接查看
github地址git

需求:github

  • 如今总共有四个页面 home profile user login,profile是我的中心的意思
  • 而后 user下面有 create-user user-list
  • 而后设置 只有登陆以后 才能去 profile user 不然就跳转到 login
  • 每一个页面都有导航条

1.加四个视图和对应的路由

  • vue create router-apply,先选择history模式好了
  • views增长 每一个页面的视图
  • router那边配置路径
  • app那边就能够写导航条了 router-link router-view
  • 坑:发现懒加载的状况下,路径不能写成变量,可是能够别名

2.user那边增长子页面

  • views增长 create-user user-list
  • router那边配置路径
  • user.vue那边增长router-view

3.user子页面能相互跳转,增长的用户跳转的时候传递到user-list

  • create-user 有input框和跳转按钮,跳转的时候,带着值,用query的方式
  • 顺便还能够设置/user路径的时候跳转到/user/create-user

4.设置只有登陆才能去profile和user

  • profile和user路径设置meta
  • 在router那边增长beforeEach钩子

核心代码展现

// main.js
router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.needLogin)) {
    // this route requires auth, check if logged in
    // if not, redirect to login page.
    if (!isLogin) {
      next({
        path: '/login',
        // 方便登陆以后返回来
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // 确保必定要调用 next()
  }
})

// router.js
 {
  path: '/user',
  name: 'user',
  component: () => import(/* webpackChunkName: "user" */ './views/User.vue'),
  children: [
    {
      path: 'create-user',
      alias: '',
      component: () => import(/* webpackChunkName: "createUser" */ './views/CreateUser.vue')
    },
    {
      path: 'user-list',
      component: () => import(/* webpackChunkName: "userList" */ './views/UserList.vue')
    }
  ],
  meta: {
    needLogin: true
  }
}
复制代码
相关文章
相关标签/搜索