Open libin1991 opened 6 years ago
将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们。
基本例子:
HTML <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')
$route(路由)为当前router跳转对象里面可以获取name、path、query、params等 $router(路由器)为VueRouter实例,想要导航到不同URL,则使用$router.push方法
动态路由配置
例如我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用『动态路径参数』(dynamic segment)来达到这个效果:
const User = { template: '<div>User</div>' } const router = new VueRouter({ routes: [ 动态路径参数 以冒号开头 { path: '/user/:id', component: User } ] }) 现在呢,像 /user/foo 和 /user/bar 都将映射到相同的路由。 一个『路径参数』使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板,输出当前用户的 ID: const User = { template: '<div>User {{ $route.params.id }}</div>' }
当使用路由参数时,两个路由都复用同个组件,比起销毁再创建,复用则显得更加高效。也意味着组件的生命周期钩子不会再被调用。 想对路由参数的变化作出响应的话,你可以简单地 watch(监测变化) $route 对象:
const User = { template: '...', watch: { '$route' (to, from) { // 对路由变化作出响应... } } }
或者使用 2.2 中引入的 beforeRouteUpdate 守卫:
const User = { template: '...', beforeRouteUpdate (to, from, next) { // react to route changes... // don't forget to call next() } }
嵌套路由
const router = new VueRouter({ 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 } ] } ] })
注意,以 / 开头的嵌套路径会被当作根路径。这让你充分的使用嵌套组件而无须设置嵌套的路径。
children 配置就是像 routes配置一样的路由配置数组,所以可以嵌套多层路由。
此时,基于上面的配置,当你访问 /user/foo 时,User 的出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个空的子路由:
const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, children: [ 当 /user/:id 匹配成功, UserHome 会被渲染在 User 的 <router-view> 中 { path: '', component: UserHome }, ...其他子路由 ] } ] })
router.push==window.history.pushState
会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
声明式 编程式 <router-link :to="..."> router.push(...)
参数可以是一个字符串路径,或者一个描述地址的对象:
// 字符串 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.push({ name: 'user', params: { userId }}) // -> /user/123 router.push({ path: `/user/${userId}` }) // -> /user/123
在 2.2.0+,可选的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。 注意:如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2),你需要使用 beforeRouteUpdate 来响应这个变化 (比如抓取用户信息)。
router.replace()==window.history.replaceState
不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。 声明式 编程式 <router-link :to="..." replace> router.replace(...)
router.go(n)==window.history.go
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步. // 在浏览器记录中前进一步,等同于 history.forward() router.go(1) // 后退一步记录,等同于 history.back() router.go(-1) // 前进 3 步记录 router.go(3)
命名视图
有时候想同时(同级)展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 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>
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置(带上 s):
const router = new VueRouter({ routes: [ { path: '/', components: { default: Foo, a: Bar, b: Baz } } ] })
嵌套命名视图
UserSettings 组件的 <template> 部分应该是类似下面的这段代码:
<template>
<!-- UserSettings.vue --> <div> <h1>User Settings</h1> <NavBar/> <router-view/> <router-view name="helper"/> </div>
嵌套的视图组件在此已经被忽略了,但是你可以在这里找到完整的源代码
然后你可以用这个路由配置完成该布局:
{ path: '/settings', // 你也可以在顶级路由就配置命名视图 component: UserSettings, children: [{ path: 'emails', component: UserEmailsSubscriptions }, { path: 'profile', components: { default: UserProfile, helper: UserProfilePreview } }] }
重定向和别名
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 重定向的 字符串路径/路径对象 }} ] })
别名
/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。 const router = new VueRouter({ routes: [ { path: '/a', component: A, alias: '/b' } ] }) 『别名』的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。
路由组件传参
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
使用 props 将组件和路由解耦:
取代与 $route 的耦合
const User = { template: '<div>User {{ $route.params.id }}</div>' } const router = new VueRouter({ routes: [ { path: '/user/:id', component: User } ] })
通过 props 解耦
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 } } ] })
这样你便可以在任何地方使用该组件,使得该组件更易于重用和测试。
布尔模式 如果 props 被设置为 true,route.params 将会被设置为组件属性。
对象模式 如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。
const router = new VueRouter({ routes: [ { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } } ] })
函数模式 你可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。
const router = new VueRouter({ routes: [ { path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) } ] })
URL /search?q=vue 会将 {query: 'vue'} 作为属性传递给 SearchUser 组件。
请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 Vue 才可以对状态变化做出反应。
HTML5 History 模式
vue-router 默认 hash 模式 可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
const router = new VueRouter({ mode: 'history', routes: [...] })
需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了, 给个警告,后台因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。
const router = new VueRouter({ mode: 'history', routes: [ { path: '*', component: NotFoundComponent } ] })
后端配置例子 Apache <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] </IfModule> 除了 mod_rewrite,你也可以使用 FallbackResource。 nginx location / { try_files $uri $uri/ /index.html; } 原生 Node.js const http = require('http') const fs = require('fs') const httpPort = 80 http.createServer((req, res) => { fs.readFile('index.htm', 'utf-8', (err, content) => { if (err) { console.log('We cannot open "index.htm" file.') } res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) res.end(content) }) }).listen(httpPort, () => { console.log('Server listening on: http://localhost:%s', httpPort) }) 基于 Node.js 的 Express 对于 Node.js/Express,请考虑使用 connect-history-api-fallback 中间件。 Internet Information Services (IIS) 安装 IIS UrlRewrite 在你的网站根目录中创建一个 web.config 文件,内容如下: <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="Handle History Mode and custom 404/500" stopProcessing="true"> <match url="(.*)" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> </conditions> <action type="Rewrite" url="/" /> </rule> </rules> </rewrite> </system.webServer> </configuration> Caddy rewrite { regexp .* to {path} / } Firebase 主机 在你的 firebase.json 中加入: { "hosting": { "public": "dist", "rewrites": [ { "source": "**", "destination": "/index.html" } ] } }
导航守卫(beforeRouteUpdate)
路由正在发生改变. 记住参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。
全局守卫(router.beforeEach)
const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { // ... }) 当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。 每个守卫方法接收三个参数: to: Route: 即将要进入的目标 路由对象 from: Route: 当前导航正要离开的路由 next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。 next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。 next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。 next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。 next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。 确保要调用 next 方法,否则钩子就不会被 resolved。
全局解析守卫(router.beforeResolve)(2.5.0 新增)
与全局前置守卫区别是router.beforeResolve在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
全局后置钩子(router.afterEach)
和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
router.afterEach((to, from) => { // ... })
路由独享的守卫(beforeEnter)
你可以在路由配置上直接定义 beforeEnter 守卫:
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ] })
组件内的守卫
beforeRouteEnter (to, from, next) { 在渲染该组件的对应路由被 confirm 前调用不!能!获取组件实例 `this`因为当守卫执行前,组件实例还没被创建,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数 }, beforeRouteEnter (to, from, next) { next(vm => { // 通过 `vm` 访问组件实例 }) }
beforeRouteUpdate (to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` // just use `this` this.name = to.params.name next() }, }
beforeRouteLeave通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。 beforeRouteLeave (to, from , next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` const answer = window.confirm('Do you really want to leave? you have unsaved changes!') if (answer) { next() } else { next(false) } }
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调.
过渡动效
<router-view> 是基本的动态组件,<transition>的所有功能 在这里同样适用:
<router-view>
<transition>
<transition> <router-view></router-view> </transition>
单个路由的过渡可以在各路由组件内使用<transition> 并设置不同的 name。
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' } }
Router api
routes:类型: Array<RouteConfig>
<RouteConfig>
declare type RouteConfig = { 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; meta?: any; // 2.6.0+ caseSensitive?: boolean; // 匹配规则是否大小写敏感?(默认值:false) pathToRegexpOptions?: Object; // 编译正则的选项 }
mode: string
默认值: "hash" (浏览器环境) | "abstract" (Node.js 环境) 配置路由模式: hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。 history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。 abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。
base: string
默认值: "/" 应用的基路径。例如,如果整个单页应用服务在 /app/ 下,然后 base 就应该设为 "/app/"。
linkActiveClass: string
默认值: "router-link-active" 全局配置 <router-link> 的默认『激活 class 类名』。参考 router-link。
linkExactActiveClass(2.5.0+): string
默认值: "router-link-exact-active" 全局配置 <router-link> 精确激活的默认的 class。可同时翻阅 router-link。
scrollBehavior : Function
type PositionDescriptor = { x: number, y: number } | { selector: string } | ?{} type scrollBehaviorHandler = ( to: Route, from: Route, savedPosition?: { x: number, y: number } ) => PositionDescriptor | Promise<PositionDescriptor> 更多详情参考滚动行为。
parseQuery / stringifyQuery(2.4.0+): Function
提供自定义查询字符串的解析/反解析函数。覆盖默认行为。
fallback(2.6.0+): boolean
当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式。默认值为 true。 在 IE9 中,设置为 false 会使得每个 router-link 导航都触发整页刷新。它可用于工作在 IE9 下的服务端渲染应用,因为一个 hash 模式的 URL 并不支持服务端渲染。
路由信息对象的属性
$route.path: string 字符串,对应当前路由的路径,总是解析为绝对路径,如 "/foo/bar"。
$route.params: Object 一个 key/value 对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
$route.query: Object 一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user == 1,如果没有查询参数,则是个空对象。
$route.hash: string 当前路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串。
$route.fullPath: string 完成解析后的 URL,包含查询参数和 hash 的完整路径。
$route.matched: Array<RouteRecord> 一个数组,包含当前路由的所有嵌套路径片段的路由记录 。路由记录就是 routes 配置数组中的对象副本 (还有在 children 数组)。 const router = new VueRouter({ routes: [ // 下面的对象就是路由记录 { path: '/foo', component: Foo, children: [ // 这也是个路由记录 { path: 'bar', component: Bar } ] } ] }) 当 URL 为 /foo/bar,$route.matched 将会是一个包含从上到下的所有对象 (副本)。
$route.name 当前路由的名称,如果有的话。(查看命名路由)
$route.redirectedFrom 如果存在重定向,即为重定向来源的路由的名字.
Props
to : string | Location <!-- 字符串 --> <router-link to="home">Home</router-link> <!-- 渲染结果 --> <a href="home">Home</a> <!-- 使用 v-bind 的 JS 表达式 --> <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>
replace: boolean 设置 replace 属性的话,当点击时,会调用 router.replace()并且不会留下 history 记录。 <router-link :to="{ path: '/abc'}" replace></router-link>
append: boolean 设置 append 属性后,则在当前(相对)路径前添加基路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b <router-link :to="{ path: 'relative/path'}" append></router-link>
tag: string 默认值: "a" <router-link to="/foo" tag="li">foo</router-link> <!-- 渲染结果 --> <li>foo</li>
active-class: string 默认值: "router-link-active" 设置 链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置。
exact: boolean "是否激活" <!-- 这个链接只会在地址为 / 的时候被激活 --> <router-link to="/" exact>
event(2.1.0+) : string | Array<string>``` 声明用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。
router-view
<router-view> 渲染的组件还可以内嵌自己的 <router-view>,根据嵌套路径,渲染嵌套组件。 name 类型: string 默认值: "default" 如果 <router-view>设置了名称,则会渲染对应的路由配置中 components 下的相应组件.所以可以配合 <transition> 和 <keep-alive> 使用。如果两个结合一起用,要确保在内层使用 <keep-alive>: <transition> <keep-alive> <router-view></router-view> </keep-alive> </transition>
将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们。
基本例子:
HTML <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')
$route(路由)为当前router跳转对象里面可以获取name、path、query、params等 $router(路由器)为VueRouter实例,想要导航到不同URL,则使用$router.push方法
动态路由配置
例如我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用『动态路径参数』(dynamic segment)来达到这个效果:
const User = { template: '<div>User</div>' } const router = new VueRouter({ routes: [ 动态路径参数 以冒号开头 { path: '/user/:id', component: User } ] }) 现在呢,像 /user/foo 和 /user/bar 都将映射到相同的路由。 一个『路径参数』使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板,输出当前用户的 ID: const User = { template: '<div>User {{ $route.params.id }}</div>' }
当使用路由参数时,两个路由都复用同个组件,比起销毁再创建,复用则显得更加高效。也意味着组件的生命周期钩子不会再被调用。 想对路由参数的变化作出响应的话,你可以简单地 watch(监测变化) $route 对象:
const User = { template: '...', watch: { '$route' (to, from) { // 对路由变化作出响应... } } }
或者使用 2.2 中引入的 beforeRouteUpdate 守卫:
const User = { template: '...', beforeRouteUpdate (to, from, next) { // react to route changes... // don't forget to call next() } }
嵌套路由
const router = new VueRouter({ 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 } ] } ] })
注意,以 / 开头的嵌套路径会被当作根路径。这让你充分的使用嵌套组件而无须设置嵌套的路径。
children 配置就是像 routes配置一样的路由配置数组,所以可以嵌套多层路由。
此时,基于上面的配置,当你访问 /user/foo 时,User 的出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个空的子路由:
const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, children: [ 当 /user/:id 匹配成功, UserHome 会被渲染在 User 的 <router-view> 中 { path: '', component: UserHome }, ...其他子路由 ] } ] })
router.push==window.history.pushState
会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
声明式 编程式 <router-link :to="..."> router.push(...)
参数可以是一个字符串路径,或者一个描述地址的对象:
// 字符串 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.push({ name: 'user', params: { userId }}) // -> /user/123 router.push({ path: `/user/${userId}` }) // -> /user/123
在 2.2.0+,可选的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。 注意:如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2),你需要使用 beforeRouteUpdate 来响应这个变化 (比如抓取用户信息)。
router.replace()==window.history.replaceState
不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。 声明式 编程式 <router-link :to="..." replace> router.replace(...)
router.go(n)==window.history.go
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步. // 在浏览器记录中前进一步,等同于 history.forward() router.go(1) // 后退一步记录,等同于 history.back() router.go(-1) // 前进 3 步记录 router.go(3)
命名视图
有时候想同时(同级)展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 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>
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置(带上 s):
const router = new VueRouter({ routes: [ { path: '/', components: { default: Foo, a: Bar, b: Baz } } ] })
嵌套命名视图
UserSettings 组件的
<template>
部分应该是类似下面的这段代码:<!-- UserSettings.vue --> <div> <h1>User Settings</h1> <NavBar/> <router-view/> <router-view name="helper"/> </div>
嵌套的视图组件在此已经被忽略了,但是你可以在这里找到完整的源代码
然后你可以用这个路由配置完成该布局:
{ path: '/settings', // 你也可以在顶级路由就配置命名视图 component: UserSettings, children: [{ path: 'emails', component: UserEmailsSubscriptions }, { path: 'profile', components: { default: UserProfile, helper: UserProfilePreview } }] }
重定向和别名
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 重定向的 字符串路径/路径对象 }} ] })
别名
/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。 const router = new VueRouter({ routes: [ { path: '/a', component: A, alias: '/b' } ] }) 『别名』的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。
路由组件传参
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
使用 props 将组件和路由解耦:
取代与 $route 的耦合
const User = { template: '<div>User {{ $route.params.id }}</div>' } const router = new VueRouter({ routes: [ { path: '/user/:id', component: User } ] })
通过 props 解耦
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 } } ] })
这样你便可以在任何地方使用该组件,使得该组件更易于重用和测试。
布尔模式 如果 props 被设置为 true,route.params 将会被设置为组件属性。
对象模式 如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。
const router = new VueRouter({ routes: [ { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } } ] })
函数模式 你可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。
const router = new VueRouter({ routes: [ { path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) } ] })
URL /search?q=vue 会将 {query: 'vue'} 作为属性传递给 SearchUser 组件。
请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 Vue 才可以对状态变化做出反应。
HTML5 History 模式
vue-router 默认 hash 模式 可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
const router = new VueRouter({ mode: 'history', routes: [...] })
需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了, 给个警告,后台因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。
const router = new VueRouter({ mode: 'history', routes: [ { path: '*', component: NotFoundComponent } ] })
后端配置例子 Apache <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] </IfModule> 除了 mod_rewrite,你也可以使用 FallbackResource。 nginx location / { try_files $uri $uri/ /index.html; } 原生 Node.js const http = require('http') const fs = require('fs') const httpPort = 80 http.createServer((req, res) => { fs.readFile('index.htm', 'utf-8', (err, content) => { if (err) { console.log('We cannot open "index.htm" file.') } res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) res.end(content) }) }).listen(httpPort, () => { console.log('Server listening on: http://localhost:%s', httpPort) }) 基于 Node.js 的 Express 对于 Node.js/Express,请考虑使用 connect-history-api-fallback 中间件。 Internet Information Services (IIS) 安装 IIS UrlRewrite 在你的网站根目录中创建一个 web.config 文件,内容如下: <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="Handle History Mode and custom 404/500" stopProcessing="true"> <match url="(.*)" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> </conditions> <action type="Rewrite" url="/" /> </rule> </rules> </rewrite> </system.webServer> </configuration> Caddy rewrite { regexp .* to {path} / } Firebase 主机 在你的 firebase.json 中加入: { "hosting": { "public": "dist", "rewrites": [ { "source": "**", "destination": "/index.html" } ] } }
导航守卫(beforeRouteUpdate)
路由正在发生改变. 记住参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。
全局守卫(router.beforeEach)
const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { // ... }) 当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。 每个守卫方法接收三个参数: to: Route: 即将要进入的目标 路由对象 from: Route: 当前导航正要离开的路由 next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。 next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。 next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。 next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。 next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。 确保要调用 next 方法,否则钩子就不会被 resolved。
全局解析守卫(router.beforeResolve)(2.5.0 新增)
与全局前置守卫区别是router.beforeResolve在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
全局后置钩子(router.afterEach)
和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
路由独享的守卫(beforeEnter)
你可以在路由配置上直接定义 beforeEnter 守卫:
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ] })
组件内的守卫
beforeRouteLeave通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。 beforeRouteLeave (to, from , next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` const answer = window.confirm('Do you really want to leave? you have unsaved changes!') if (answer) { next() } else { next(false) } }
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调.
过渡动效
<router-view>
是基本的动态组件,<transition>
的所有功能 在这里同样适用:单个路由的过渡可以在各路由组件内使用
<transition>
并设置不同的 name。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' } }
Router api
routes:类型: Array
<RouteConfig>
declare type RouteConfig = { 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; meta?: any; // 2.6.0+ caseSensitive?: boolean; // 匹配规则是否大小写敏感?(默认值:false) pathToRegexpOptions?: Object; // 编译正则的选项 }
mode: string
默认值: "hash" (浏览器环境) | "abstract" (Node.js 环境) 配置路由模式: hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。 history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。 abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。
base: string
默认值: "/" 应用的基路径。例如,如果整个单页应用服务在 /app/ 下,然后 base 就应该设为 "/app/"。
linkActiveClass: string
默认值: "router-link-active" 全局配置 <router-link> 的默认『激活 class 类名』。参考 router-link。
linkExactActiveClass(2.5.0+): string
默认值: "router-link-exact-active" 全局配置 <router-link> 精确激活的默认的 class。可同时翻阅 router-link。
scrollBehavior : Function
type PositionDescriptor = { x: number, y: number } | { selector: string } | ?{} type scrollBehaviorHandler = ( to: Route, from: Route, savedPosition?: { x: number, y: number } ) => PositionDescriptor | Promise<PositionDescriptor> 更多详情参考滚动行为。
parseQuery / stringifyQuery(2.4.0+): Function
fallback(2.6.0+): boolean
当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式。默认值为 true。 在 IE9 中,设置为 false 会使得每个 router-link 导航都触发整页刷新。它可用于工作在 IE9 下的服务端渲染应用,因为一个 hash 模式的 URL 并不支持服务端渲染。
路由信息对象的属性
$route.path: string 字符串,对应当前路由的路径,总是解析为绝对路径,如 "/foo/bar"。
$route.params: Object 一个 key/value 对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
$route.query: Object 一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user == 1,如果没有查询参数,则是个空对象。
$route.hash: string 当前路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串。
$route.fullPath: string 完成解析后的 URL,包含查询参数和 hash 的完整路径。
$route.matched: Array<RouteRecord> 一个数组,包含当前路由的所有嵌套路径片段的路由记录 。路由记录就是 routes 配置数组中的对象副本 (还有在 children 数组)。 const router = new VueRouter({ routes: [ // 下面的对象就是路由记录 { path: '/foo', component: Foo, children: [ // 这也是个路由记录 { path: 'bar', component: Bar } ] } ] }) 当 URL 为 /foo/bar,$route.matched 将会是一个包含从上到下的所有对象 (副本)。
$route.name 当前路由的名称,如果有的话。(查看命名路由)
$route.redirectedFrom 如果存在重定向,即为重定向来源的路由的名字.
Props
to : string | Location <!-- 字符串 --> <router-link to="home">Home</router-link> <!-- 渲染结果 --> <a href="home">Home</a> <!-- 使用 v-bind 的 JS 表达式 --> <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>
replace: boolean 设置 replace 属性的话,当点击时,会调用 router.replace()并且不会留下 history 记录。 <router-link :to="{ path: '/abc'}" replace></router-link>
append: boolean 设置 append 属性后,则在当前(相对)路径前添加基路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b <router-link :to="{ path: 'relative/path'}" append></router-link>
tag: string 默认值: "a" <router-link to="/foo" tag="li">foo</router-link> <!-- 渲染结果 --> <li>foo</li>
active-class: string 默认值: "router-link-active" 设置 链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置。
exact: boolean "是否激活" <!-- 这个链接只会在地址为 / 的时候被激活 --> <router-link to="/" exact>
router-view
<router-view> 渲染的组件还可以内嵌自己的 <router-view>,根据嵌套路径,渲染嵌套组件。 name 类型: string 默认值: "default" 如果 <router-view>设置了名称,则会渲染对应的路由配置中 components 下的相应组件.所以可以配合 <transition> 和 <keep-alive> 使用。如果两个结合一起用,要确保在内层使用 <keep-alive>: <transition> <keep-alive> <router-view></router-view> </keep-alive> </transition>