Vue:vue-router

本文参照vue-router官网进行记录与学习的。

vue-router

vue-router实现Vue的路由,我们已经可以通过组合组件来组成应用程序,当要把 vue-router 添加进来的时候,需要做的是,将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们(通过<router-view>),这句话挺重要的。

安装

在 Vue 后面加载 vue-router,它会自动安装的:

<script src="/path/to/vue.js"></script>
<script src="/path/to/vue-router.js"></script>

npm:

npm install vue-router --save-dev
import Router from 'vue-router'

导入进来之后,使用Vue.use(Router)API注册此插件,然后就实例化使用:

import Vue from 'vue'
import Router from 'vue-router' // 导入

Vue.use(Router) // 注册

export default new Router({ // 实例化
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    }
  ]
})

官网的一个案例(html):

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

官网的一个案例(JavaScript):

// 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 },
  { path: '/bar', component: Bar }
]

// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
  routes // (缩写)相当于 routes: routes
})

// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
  router
}).$mount('#app')

// 现在,应用已经启动了!

从上面的例子引入第一个知识点,往下看 ↓↓↓

router的两个节点

这个插件只有两个节点:

  • <router-link>:用来生成HTML标签(默认<a>),并且达到点击URL跳转的效果,支持用户在具有路由功能的应用中(点击)导航。

    • to:属性指定目标地址(必须)
    • tag:可以更改默认生成的HTML标签。
    • replace:会调用router.replace()而不是router.push(),于是导航后不会留下history记录(直接在标签后追加这个key,没有值)
    • append:在当前(相对)路径前添加基路径,例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b(直接在标签后追加这个key,没有值)
    • active-class:当目标路由成功激活时,还会自动配置一个已经激活的CSS类名,默认“router-link-active”,通过路由的构造选项linkActiveClass来全局配置
    • exact:是否激活,只有匹配此值修饰的标签的to路径才会激活
    • event:声明可以触发导航的事件,可以是一个字符串或者包含字符串的数组,默认click就能触发导航(触发就是跳转)
    • exact-active-class:配置当链接被精确匹配的时候应该激活的 class。注意默认值也是可以通过路由构造函数选项 linkExactActiveClass 进行全局配置的。

声明式:

  <!-- 字符串 -->
  <router-link to="home">Home</router-link>
  <!-- 渲染结果 -->
  <a href="home">Home</a>

  <!-- 使用 v-bind 的 JS 表达式 -->
  <router-link v-bind:to="'home'">Home</router-link>

  <!-- 不写 v-bind 也可以,就像绑定别的属性一样 -->
  <router-link :to="'home'">Home</router-link>

  <!-- 同上 -->
  <router-link :to="{ path: 'home' }">Home</router-link>

  <!-- 命名的路由 -->
  <router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

  <!-- 带查询参数,下面的结果为 /register?plan=private -->
  <router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
<router-link :to="{path: '/zeng'}">to zeng</router-link>
<router-link :to="{path: 'b'}" append>to b</router-link> <!-- /zeng/b -->
<!-- 双击后才跳转 -->
<router-link :to="{path: '/zeng'}" event="dblclick">to zeng</router-link>

编程式:

<!-- 字符串 --> 
router.push('home')

<!-- 对象 --> 
router.push({ path: 'home' })

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

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

如果提供了 pathparams 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path

const userId = 123
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

vue-router 的导航方法 (pushreplacego) 在各类路由模式(historyhashabstract)下表现一致。

  • <router-view>:是一个 functional 组件,渲染路径匹配到的视图组件。<router-view> 渲染的组件还可以内嵌自己的 <router-view>,根据嵌套路径,渲染嵌套组件。

    • name:如果 <router-view> 设置了名称,则会渲染对应的路由配置中 components 下的相应组件。
<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>

const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: Foo, <!-- 匿名 -->
        a: Bar, <!-- 匹配 a -->
        b: Baz <!-- 匹配 b -->
      }
    }
  ]
})

functional组件是一个无实例、无上下文的组件,只用做渲染

router的构造配置

Array routes:

path: string; // 路径
component: Component; // 路径对应的模板
name: string; // 命名路由
components: { [name: string]: Component }; // 命名视图组件
redirect: string | Location | Function; // 路径重定向
props: boolean | string | Function;
alias: string | Array<string>; // 别名
children: Array<RouteConfig>; // 嵌套路由
beforeEnter: (to: Route, from: Route, next: Function) => void; // 导航守卫钩子:https://router.vuejs.org/zh-cn/advanced/navigation-guards.html
meta: any; // 路由元信息:https://router.vuejs.org/zh-cn/advanced/meta.html

// 2.6.0+
caseSensitive: boolean; // 匹配规则是否大小写敏感?(默认值:false)
pathToRegexpOptions: Object; // 编译正则的选项

string mode:(配置路由模式)

  • hash:(浏览器环境)使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。
  • history: 依赖 HTML5 History API 和服务器配置。
  • abstract: (node.js环境)支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。

string base:默认值“/”,应用的基路径。例如,如果整个单页应用服务在 /app/ 下,然后 base 就应该设为 "/app/"

string linkActiveClass/linkExactActiveClass: 上面提到过。

function scrollBehavior: 滚动策略(路由之后是回到浏览器顶端还是保持原位置不动),参考链接

function parseQuery / stringifyQuery: 提供自定义查询字符串的解析/反解析函数。覆盖默认行为。

boolean fallback: 当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式。默认值为 true。在 IE9 中,设置为 false 会使得每个 router-link 导航都触发整页刷新。它可用于工作在 IE9 下的服务端渲染应用,因为一个 hash 模式的 URL 并不支持服务端渲染。

alias

『重定向』的意思是,当用户访问 /a 时,URL 将会被替换成 /b,然后匹配路由为 /b/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b但是路由匹配则为 /a,就像用户访问 /a 一样。

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

props

在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。props就是为了解决这个问题。

方法模式

方法模式接收一个参数,里面包含了请求对象的信息

new Router({
    routes: [
        {
          name: 'test1',
          path: '/zeng',
          component: zeng,
          props: (route) => {
            //业务处理
            console.log('route', route)
          },
          children: [
            {
              path: 'imain', /* 不能加“/” */
              component: test,
              components: {
                zeng,
                test,
                h: HelloWorld
              }
            }
          ],
        }
    ]
})

请求此路径

<router-link :to="{ name: 'test1', params: {color: 'red', name: '吴彦祖', age: 18} }">to zeng</router-link>

控制台打印的信息:

布尔模式

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})

对象模式

const router = new VueRouter({
  routes: [
    { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
  ]
})

点击查看官网给的案例

动态路由和命名路由

我们经常需要根据不同的参数匹配同一个模板进行不同的展示,比如根据传过来的color值来对同一模板设置不同的背景颜色,那么第一步要做的事情是需要在vue-router实例化的地方设置路径:

new Router({
  mode: 'history',
  routes: [
    {
      path: '/zeng/:color', <!-- 动态路径的方式 -->
      component: zeng
    },
  ]
})

第二步在<router-link>中指定/:color的值:

<router-link :to="{path: '/zeng/black'}">to zeng and set background-color</router-link>

在这里指定的值会传给目标模板一些参数,这些传过去的参数都可以通过this.$route.params拿到,然后在目标模板中对这些参数进行操作:

// 钩子函数
mounted () {
  let body = document.getElementsByTagName('body')[0];
  body.style.backgroundColor = this.color;
}

上面这种动态路由的参数会在URL上显示,还有一种不显示的,步骤如下:

new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/test',
      component: test
    },
    {
      // 只有[命名路由]才能完成这个目的,在<router-link>中,path和params是相冲突的,改变1
      name: 'test1', 
      path: '/zeng', // 改变2
      component: zeng,
      children: [
        {
          path: 'imain', /* 不能加“/” */
          component: test
        }
      ]
    },

  ]
})
<!-- 改变3 这里通过params传给test1对应的模板的参数 -->
<router-link :to="{ name: 'test1', params: {color: 'red', name: '吴彦祖', age: 18} }">to zeng</router-link>

然后在模板模板上打印this.$route.params

但是,这个对比可以显示参数的形式来说,一刷新页面这些参数就会丢失,需要重新激活导航传递参数。

注意

原来的组件实例会被复用,这也意味着组件的生命钩子函数将不会被触发,如果要触发需要用watch监听:

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

或者

const User = {
  template: '...',
  beforeRouteUpdate (to, from, next) {
    // react to route changes...
    // don't forget to call next()
  }
}

嵌套路由

使用children属性来完成路由的嵌套:

new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/test',
      component: test
    },
    {
      path: '/zeng/:color',
      component: zeng,
      children: [
        {
          path: 'imain', /* 不能加“/” */
          component: test
        }
      ]
    },

  ]
})

访问:localhost:8080/zeng/red/imain 就能看到两个组件,子组件test需要在父组件zeng中添加<router-view/>,子组件的内容只会出现在父组件中<router-view/>的位置,并不会覆盖父组件的内容。并且children里面也是可以继续嵌套的。

命名视图

有时候想同时(同级)展示多个视图,而不是嵌套展示,就可以使用components,它有一个default的模板:

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

要记得加s

const router = new VueRouter({
  routes: [
    {
      path: '/',
      redirect: '/login', <!-- 路径重定向到/login上,接收一个string或者function(to) to是重定向后的目标,如果是方法就 return 重定向的 字符串路径/路径对象 -->
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})

过滤效果

不知道为什么,对css的动画和过滤非常的喜欢,可能这就是爱吧(今天情人节),单个l路由过滤,在创建模板的时候就给模板内容设置一个过滤效果:

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

const Bar = {
  template: `
    <transition name="fade">
      <div class="bar">...</div>
    </transition>
  `
}

基于路由切换的过滤:

<!-- 使用动态的 transition name -->
<transition :name="transitionName">
  <router-view></router-view>
</transition>
// 接着在父组件内
// watch $route 决定使用哪种过渡
watch: {
  '$route' (to, from) {
    const toDepth = to.path.split('/').length
    const fromDepth = from.path.split('/').length
    this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
  }
}

路由懒加载

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

结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

首先,可以将异步组件定义为返回一个 Promise 的工厂函数 (该函数返回的 Promise 应该 resolve 组件本身):

const Foo = () => Promise.resolve({ /* 组件定义对象 */ }) // 参照 http://imain.net/index.php/archives/135/

第二,在 Webpack 2 中,我们可以使用动态 import 语法来定义代码分块点 (split point):

import('./Foo.vue') <!-- 返回 Promise --> 

<!-- const Foo = () => import('./Foo.vue') --> 

在路由配置中什么都不需要改变,只需要像往常一样使用 Foo:

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

end ...

Last modification:February 14th, 2018 at 02:26 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment