vue router

简单的路由原理,基于组件

const NotFound = { template: '

Page not found

' } const Home = { template: '

home page

' } const About = { template: '

about page

' } const routes = { '/': Home, '/about': About } new Vue({ el: '#app', data: { currentRoute: window.location.pathname }, computed: { ViewComponent () { return routes[this.currentRoute] || NotFound } }, render (h) { return h(this.ViewComponent) } })

vue-router学习

1.使用

npm install vue-router

import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

2.入口

Hello App!

Go to Foo Go to Bar

3.JavaScript

// 0. 如果使用模块化机制编程, 要调用 Vue.use(VueRouter)

// 1. 定义(路由)组件。
// 可以从其他文件 import 进来
const Foo = { template: '
foo
' } const Bar = { template: '
bar
' } // 2. 定义路由 // 每个路由应该映射一个组件。 其中"component" 可以是 // 通过 Vue.extend() 创建的组件构造器, // 或者,只是一个组件配置对象。 // 我们晚点在讨论嵌套路由。 const routes = [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar } ] // 3. 创建 router 实例,然后传 `routes` 配置 // 你还可以传别的配置参数, 不过先这么简单着吧。 const router = new VueRouter({ routes // (缩写)相当于 routes: routes }) // 4. 创建和挂载根实例。 // 记得要通过 router 配置参数注入路由, // 从而让整个应用都有路由功能 const app = new Vue({ router }).$mount('#app') // 现在,应用已经启动了!

动态路由监听
$route.params.

const User = {
     template: '...',
     watch: { 
        '$route' (to, from) { 
           // 对路由变化作出响应... 
        }
     }
}

4.嵌套路由

/user/foo /user/foo/profile /user/foo/posts

const User = { template: `

User {{ $route.params.id }}

` } const UserHome = { template: '
Home
' } const UserProfile = { template: '
Profile
' } const UserPosts = { template: '
Posts
' } const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, children: [ // UserHome will be rendered inside User's // when /user/:id is matched { path: '', component: UserHome }, // UserProfile will be rendered inside User's // when /user/:id/profile is matched { path: 'profile', component: UserProfile }, // UserPosts will be rendered inside User's // when /user/:id/posts is matched { path: 'posts', component: UserPosts } ] } ] })

5.编程式的导航

Paste_Image.png
// 字符串
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.replace(location)

跟 router.push
很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

Paste_Image.png

router.go(n)

// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)

// 后退一步记录,等同于 history.back()
router.go(-1)

// 前进 3 步记录
router.go(3)

// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)

6.命名路由

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const Home = { template: '
This is Home
' } const Foo = { template: '
This is Foo
' } const Bar = { template: '
This is Bar {{ $route.params.id }}
' } const router = new VueRouter({ mode: 'history', base: __dirname, routes: [ { path: '/', name: 'home', component: Home }, { path: '/foo', name: 'foo', component: Foo }, { path: '/bar/:id', name: 'bar', component: Bar } ] }) new Vue({ router, template: `

Named Routes

Current route name: {{ $route.name }}

  • home
  • foo
  • bar
` }).$mount('#app')

7.命名视图

(同级)展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar
(侧导航) 和main
(主内容) 两个视图,这个时候命名视图就派上用场了




Named Views

  • /
  • /other
const Foo = { template: '
foo
' } const Bar = { template: '
bar
' } const Baz = { template: '
baz
' } const router = new VueRouter({ mode: 'history', routes: [ { path: '/', // a single route can define multiple named components // which will be rendered into s with corresponding names. components: { default: Foo, a: Bar, b: Baz } }, { path: '/other', components: { default: Baz, a: Bar, b: Foo } } ] }) new Vue({ router, el: '#app' })
vue router_第1张图片
Paste_Image.png

8.重定向 和 别名

重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})
重定向的目标也可以是一个命名的路由:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})
甚至是一个方法,动态返回重定向目标:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }}
  ]
})
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const Home = { template: '

Home

' } const Foo = { template: '
foo
' } const Bar = { template: '
bar
' } const Baz = { template: '
baz
' } const router = new VueRouter({ mode: 'history', base: __dirname, routes: [ { path: '/home', component: Home, children: [ // absolute alias { path: 'foo', component: Foo, alias: '/foo' }, // relative alias (alias to /home/bar-alias) { path: 'bar', component: Bar, alias: 'bar-alias' }, // multiple aliases { path: 'baz', component: Baz, alias: ['/baz', 'baz-alias'] } ] } ] }) new Vue({ router, template: `

Route Alias

  • /foo (renders /home/foo)
  • /home/bar-alias (renders /home/bar)
  • /baz (renders /home/baz)
  • /home/baz-alias (renders /home/baz)
` }).$mount('#app')

1.导航钩子

vue-router
提供的导航钩子主要用来拦截导航,让它完成跳转或取消。有多种方式可以在路由导航发生时执行钩子:全局的, 单个路由独享的, 或者组件级的。
(1)全局钩子

const router = new VueRouter({ ... })

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

每个钩子方法接收三个参数:
**to: Route
**: 即将要进入的目标 路由对象

**from: Route
**: 当前导航正要离开的路由

**next: Function
**: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next
方法的调用参数。

**next()
**: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 **confirmed** 
(确认的)。

**next(false)
**: 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),
那么 URL 地址会重置到 from 路由对应的地址。

**next('/')
 或者 next({ path: '/' })
**: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。

(2)某个路由独享的钩子
你可以在路由配置上直接定义 beforeEnter 钩子:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

(3)组件内的钩子
最后,你可以使用 beforeRouteEnter 和 beforeRouteLeave,在路由组件内直接定义路由导航钩子,

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) => {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当钩子执行前,组件实例还没被创建
  },
  beforeRouteLeave (to, from, next) => {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

beforeRouteEnter 钩子 不能 访问 this,因为钩子在导航确认前被调用,因此即将登场的新组件还没被创建。
不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

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

可以 在 beforeRouteLeave 中直接访问 this。这个 leave 钩子通常用来禁止用户在还未保存修改前突然离开。可以通过 next(false) 来取消导航。

2.路由元信息

定义路由的时候可以配置 meta 字段:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})

下面例子展示在全局导航钩子中检查 meta 字段:

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()
  }
})

3.滚动特效

是基本的动态组件,所以我们可以用
组件给它添加一些过渡效果:

 
    

单个路由的过渡

const Foo = {
  template: `
    
      
...
` } const Bar = { template: `
...
` }

动态路由

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const Home = {
  template: `
    

Home

hello

` } const Parent = { data () { return { transitionName: 'slide-left' } }, // dynamically set transition based on route change watch: { '$route' (to, from) { const toDepth = to.path.split('/').length const fromDepth = from.path.split('/').length this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left' } }, template: `

Parent

` } const Default = { template: '
default
' } const Foo = { template: '
foo
' } const Bar = { template: '
bar
' } const router = new VueRouter({ mode: 'history', base: __dirname, routes: [ { path: '/', component: Home }, { path: '/parent', component: Parent, children: [ { path: '', component: Default }, { path: 'foo', component: Foo }, { path: 'bar', component: Bar } ] } ] }) new Vue({ router, template: `

Transitions

  • /
  • /parent
  • /parent/foo
  • /parent/bar
` }).$mount('#app')

4.导航完成后获取数据

假设我们有一个 Post 组件,需要基于 $route.params.id 获取文章数据:
导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示『加载中』之类的指示。

导航完成之前获取:导航完成前,在路由的 enter
钩子中获取数据,在数据获取成功后执行导航。


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
        }
      })
    }
  }
}

在导航完成前获取数据

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

5.滚动行为

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const Home = { template: '
home
' } const Foo = { template: '
foo
' } const Bar = { template: `
bar

Anchor

` } // scrollBehavior: // - only available in html5 history mode // - defaults to no scroll behavior // - return false to prevent scroll const scrollBehavior = (to, from, savedPosition) => { if (savedPosition) { // savedPosition is only available for popstate navigations. return savedPosition } else { const position = {} // new navigation. // scroll to anchor by returning the selector if (to.hash) { position.selector = to.hash } // check if any matched route config has meta that requires scrolling to top if (to.matched.some(m => m.meta.scrollToTop)) { // cords will be used if no selector is provided, // or if the selector didn't match any element. position.x = 0 position.y = 0 } // if the returned position is falsy or an empty object, // will retain current scroll position. return position } } const router = new VueRouter({ mode: 'history', base: __dirname, scrollBehavior, routes: [ { path: '/', component: Home, meta: { scrollToTop: true }}, { path: '/foo', component: Foo }, { path: '/bar', component: Bar, meta: { scrollToTop: true }} ] }) new Vue({ router, template: `

Scroll Behavior

  • /
  • /foo
  • /bar
  • /bar#anchor
` }).$mount('#app')
6.路由懒加载

结合 Vue 的 异步组件 和 Webpack 的 code splitting feature, 轻松实现路由组件的懒加载。

我们要做的就是把路由对应的组件定义成异步组件:

const Foo = resolve => {
  // require.ensure 是 Webpack 的特殊语法,用来设置 code-split point
  // (代码分块)
  require.ensure(['./Foo.vue'], () => {
    resolve(require('./Foo.vue'))
  })
}
这里还有另一种代码分块的语法,使用 AMD 风格的 require,于是就更简单了:

const Foo = resolve => require(['./Foo.vue'], resolve)
不需要改变任何路由配置,跟之前一样使用 Foo:

const router = new VueRouter({
  routes: [
    { path: '/foo', component: Foo }
  ]
})

把组件按组分块

const Foo = r => require.ensure([], () => r(require('./Foo.vue')), 'group-foo')
const Bar = r => require.ensure([], () => r(require('./Bar.vue')), 'group-foo')
const Baz = r => require.ensure([], () => r(require('./Baz.vue')), 'group-foo')

你可能感兴趣的:(vue router)