总结
<router-link>
- 可以通过配置 tag 属性生成别的标签,默认为 a 标签
- 当目标路由成功激活时,链接元素自动设置一个表示激活的 CSS 类名.router-link-active
- 无论是 HTML5 history 模式还是 hash 模式,它的表现行为一致,所以,当你要切换路由模式,或者在 IE9 降级使用 hash 模式,无须作任何变动。
- 在 HTML5 history 模式下,router-link 会守卫点击事件,让浏览器不再重新加载页面。
- 当你在 HTML5 history 模式下使用 base 选项之后,所有的 to 属性都不需要写 (基路径) 了。
- 注释:基路径是指具有path的路径片段,例如:当
base:'/app'
时,需要访问/app/about
时只需要输入to="/about"
即可
- 注释:
to='about'
头部不含/
的路径,是相对当前url的相对路径。默认行为是删除最后一个path然后拼接当前to指向的字符串上去。例如:当前url为http://localhost:8080/
会首先删除''
然后拼接成http://localhost:8080/about
v-slot API (3.1.0 新增)
- router-link 通过一个作用域插槽暴露底层的定制能力。
- 在使用 v-slot API 时,需要向 router-link 传入一个单独的子元素。否则 router-link 将会把子元素包裹在一个 span 元素内。
- href:解析后的 URL。将会作为一个 a 元素的 href attribute。
- route:解析后的规范化的地址。注释:就是当前route对象
- navigate:触发导航的函数。会在必要时自动阻止事件,和 router-link 同理。
- 注释:即执行navigate(e)路由被确认,执行页面跳转。需要传入事件对象为参数
- isActive:如果需要应用激活的 class 则为 true。允许应用一个任意的 class。
- 注释:被激活的是当前url所包含的所有片段,例如:当前路由为
http://localhost:8080/about/456
时http://localhost:8080/
的isActive为true
- isExactActive:如果需要应用精确激活的 class 则为 true。允许应用一个任意的 class。
- 如果你在 <a> 元素上添加一个 target="_blank",则 @click="navigate" 处理器会被忽略。
<router-link
to="/about"
v-slot="{ href, route, navigate, isActive, isExactActive }"
>
<NavLink :active="isActive" :href="href" @click="navigate"
>{{ route.fullPath }}</NavLink
>
</router-link>
<router-link> Props
to
- 类型: string | Location
- required
- 如果提供了 path,params 会被忽略
- 注释:当变量存在多个时,例如:
/:id+
,那么需要params.id===[123,456]
可以生成/123/456
的路径,并避免/被转义。不过这时,porops.id传入的就是数组而不是字符串
<router-link to="home">Home</router-link>
<router-link :to="{ path: 'home' }">Home</router-link>
<router-link :to="{ name: 'user', params: { userId: 123 }, query: { plan: 'private' }}">User</router-link>
replace
<router-link :to="{ path: '/abc'}" replace></router-link>
append
- 类型: boolean
- 默认值: false
- 设置 append 属性后,则在当前 (相对) 路径前添加基路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b
- 注释:可以视为完整保留当前url然后在后面添加to指向的相对路径,当设置为以 / 开头的绝对路径时这个属性是无效的
tag
- 类型: string
- 默认值: "a"
- 使用 tag prop 类指定何种标签,同样它还是会监听点击,触发导航
active-class
- 类型: string
- 默认值: "router-link-active"
- 设置链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置。
exact
- 类型: boolean
- 默认值: false
- “是否激活”默认类名的依据是包含匹配。 想要链接使用“精确匹配模式”,则使用 exact 属性
- 注释:未设置该属性时,如果该链接被激活会添加 router-link-active 类名,当添加这个属性后只有当链接被精确匹配才会添加类名,而且类名是 router-link-exact-active
<!-- 这个链接只会在地址为 / 的时候被激活 -->
<router-link to="/" exact></router-link>
event
- 类型: string | Array
- 默认值: 'click'
- 声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。
exact-active-class
- 类型: string
- 默认值: "router-link-exact-active"
- 配置当链接被精确匹配的时候应该激活的 class。注意默认值也是可以通过路由构造函数选项 linkExactActiveClass 进行全局配置的
——————————————————————————————————————————————————————————————————
<router-view>
- 其他属性 (非 router-view 使用的属性) 都直接传给渲染的组件
- 因为它也是个组件,所以可以配合 <transition> 和 <keep-alive> 使用。如果两个结合一起用,要确保在内层使用 <keep-alive>
- 如果你想让每个路由组件有各自的过渡效果,可以在各路由组件内使用 <transition> 并设置不同的 name。
- 也可以在<router-view>外嵌套<transition>并绑定变量name,通过 watch $route 决定使用哪种过渡
<router-view> Props
name
- 类型: string
- 默认值: "default"
——————————————————————————————————————————————————————————————————
Router 构建选项
- 注释:返回一个 router 对象
const router = new VueRouter({...})
routes
- 类型: Array<RouteConfig>
- routes 配置中的每个路由对象为 路由记录
- 当一个路由匹配成功后,他可能匹配多个路由记录
interface RouteConfig = {
path: string, //
component?: Component, // 可以是通过 Vue.extend() 创建的组件构造器,或者,只是一个组件配置对象。
name?: string, // 命名路由
components?: { [name: string]: Component }, // 命名视图组件
redirect?: string | Location | Function, // 重定向
props?: boolean | Object | Function, // 动态路径参数作为组件props
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 // 编译正则的选项
}
- path 详解
- 可以多种参数类型、常量混搭,例如
/(apple-)?icon-:res(\d+).png
// 动态路径参数 以冒号开头。
{ path: '/user/:id', component: User }
// 可以在一个路由中设置多段“路径参数”,对应的值都会设置到 $route.params 中。
/user/:username/post/:post_id /user/evan/post/123 { username: 'evan', post_id: '123' }
// 注释:*代表该变量有0个或者多个,例如:该例子中能够匹配 /user/123 、 /user/ 、 /user
// 注释:动态路由参数只能匹配一对间隔符号之间的值,如果有多个间隔符就需要*来匹配。例如:该例子中匹配/user/123/456,这时foo==='123/456'
// 注释:/user/:foo* 并不等价于/user(/:foo)* 。原因不明
// 注释:'/foo/:bar' 被解析为 /^/foo/([^/]+?)/?$/i 用来对路径进行匹配。[^/]表示非/字符,+?表示非贪婪匹配,非贪婪匹配
/user/:foo*
// a param can be made optional by adding "?" 注释:可选参数
{ path: '/optional-params/:foo?' },
// 匹配一个或多个 +
/:foo+
// 注释:带有正则验证,默认的 [^/]+,所以动态路径只能匹配一组分隔符间的值
{ path: '/params-with-regex/:id(\d+)' },
// 注释:可以只写正则作为未命名参数,该path 可以匹配'/test/route'
/:foo/(.*)
// 会匹配所有路径,包括/
path: '*'
// 会匹配以 `/user-` 开头的任意路径,不能够匹配不包含user-的路径
path: '/user-*'
// 注释:可选路径,常量时和其他配合时需要用()包裹
// 注释:当是常量时params通过数值key传入参数,而且所有参数必须符合[A-Za-z0-9_],所以这里的'foo/'是无法通过params传入的,所以建议改为'{ path: '/optional-group/(foo)?/bar' }'
// 注释:'{ path: '/optional-group/(foo)+/bar' }'依然无法在name跳转时通过params传入多个值,有未命名路径参数时通过pathMatch或数值key通过params传入
{ path: '/optional-group/(foo/)?bar' }
- children详解
- 以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。
- 没有匹配到合适的子路由,出口是不会渲染任何东西,如果你想要渲染点什么,可以提供一个 空的 子路由
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{ path: '', component: UserHome },
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
}
]
}
]
})
- redirect详解
- 注释:重定向后的参数可以是定值,例如:
{ path: '/redirect-with-params/:id', redirect: '/with-params/456' }
这个时候params.id === '456'
- 注释:重定向前后的path段不需要完全匹配,例如:
{ path: '/redirect-with-params/other-path/:id', redirect: '/with-params/:id' }
这个时候id依然能够正常传递
- 注释:重定向的参数依据定向前参数名传递,而不是依据定向目标url参数名。例如目标 path 为
/with-params/:id
当前route可写为{ path: '/redirect-with-params/:other-id', redirect: '/with-params/:other-id' }
- 注释:重定向前的url不会写入history中?
- 注释:如果当前路由是目标路由重定向后的路由,该跳转是不会被执行的,也不存在又一次重定向。
- 注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。
- 注释:重定向会自动带上?及查询参数
const router = new VueRouter({
routes: [
{ path: '/redirect-with-params/:id', redirect: '/with-params/:id' }
]
})
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/root', component: Root, alias: '/root-alias' },
{ 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'] },
// default child route with empty string as alias. 空字符串别名
{ path: 'default', component: Default, alias: '' },
// nested alias 嵌套别名,/home/nested-alias/foo 等同于访问 /home/nested/foo
{ path: 'nested', component: Nested, alias: 'nested-alias',
children: [
{ path: 'foo', component: NestedFoo }
]
}
]
}
]
})
// 区分大小写,注释:谷歌输入URL会自动转为小写
{ path: '/foobar', component: Foobar, caseSensitive: true }
- pathToRegexpOptions详解
- sensitive: When true the route will be case sensitive. (default: false) true时区分大小写
- strict: When false the trailing slash is optional. (default: false) false时尾部斜杠在正则中为可有可无
- end: When false the path will match at the beginning. (default: true) false时正则将拥有,疑问:好像true才是拥有
- delimiter: Set the default delimiter for repeat parameters. (default: '/') 设置默认间隔符号
// 编译正则的选项,这里应该是区分大小写,不知道还有哪些
{ path: '/FooBar', component: FooBar, pathToRegexpOptions: { sensitive: true }}
- props详解
- 如果routes中使用component标明组件, 且 props 是一个对象,它会被按原样设置为组件props(为props传入固定值)
- 请尽可能保持 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
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
- component详解,包括components中的
- 可以将异步组件定义为返回一个 Promise 的工厂函数 (该函数返回的 Promise 应该 resolve 组件本身)
- 注释:Babel 把 import() 解析为 Promise 对象。而 Webpack 使用该标志来进行代码分块,注释语句也是有 Webpack 提供。
- 把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)
- Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。
- 这个工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器得到组件定义的时候被调用
- 可以调用 reject(reason) 来表示加载失败。
- 这里的异步组件工厂函数也可以返回一个如下格式的对象:(2.3.0+ 新增)
- 如果你希望在 Vue Router 的路由组件中使用上述语法的话,你必须使用 Vue Router 2.4.0+ 版本。
// promise
component:() => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
// 工厂函数返回组件配置对象
component:function (resolve, reject) {
resolve({
template: '<div>I am async!</div>'
})
}
// 或构造函数
component:Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 工厂函数返回特定对象
component: () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
mode
- 类型: string
- 默认值: "hash" (浏览器环境) | "abstract" (Node.js 环境)
- 可选值: "hash" | "history" | "abstract"
- abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。
base
- 类型: string
- 默认值: "/"
- 应用的基路径。例如,如果整个单页应用服务在 /app/ 下,然后 base 就应该设为 "/app/"
linkActiveClass
- 类型: string
- 默认值: "router-link-active"
- 类型: Function
- 这个功能只在支持 history.pushState 的浏览器中可用
- 第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。
- 注释:scrollBehavior只有在路由切换时才触发,通过url进入页面不会触发
- 可以利用路由元信息更细颗粒度地控制滚动。
- 可以返回一个 Promise 来得出预期的位置描述(2.8.0 新增),可以用于等待页面动画结束之后才触发滚动(见之后的例子)
type PositionDescriptor =
{ x: number, y: number } |
{ selector: string, offset? : { x: number, y: number }} | // offset 只在 2.6.0+ 支持
?{} // 如果返回一个 falsy 的值,或者是一个空对象,那么不会发生滚动。
type scrollBehaviorHandler = (
to: Route,
from: Route,
savedPosition?: { x: number, y: number }
) => PositionDescriptor | Promise<PositionDescriptor>
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// savedPosition
return savedPosition
// 滚动到锚点
return {
selector: to.hash
}
// 偏离
return {
offset: { y: 100 }
}
// promise
return new Promise(resolve => {...})
}
})
parseQuery / stringifyQuery
- 类型: Function
- 提供自定义查询字符串的解析/反解析函数。覆盖默认行为。
- 注释:parseQuery 接收 url 传入的查询条件,不包含?当通过非path方式跳转时,parseQuery 不被执行。
- 注释:stringifyQuery ,当
to:{...,query:{...}}}
时接收query传入的对象。当为path时接收parseQuery 的返回值。该函数的返回值作为url的查询参数,需要包含?。当通过to:{...,query:{...}}}
方式跳转时,返回值不会替换url的查询参数
- 注释:通过url访问页面时,先执行当前页面的parseQuery 和stringifyQuery 。然后执行所有路由的这两个函数
- 注释:当点击同一个路由时只执行当前路由的stringifyQuery
- 注释:当通过路由跳转时,先执行当前路由的stringifyQuery ,然后执行所有路由的这两个函数
fallback
- 类型: boolean
- 当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式。默认值为 true。
- 在 IE9 中,设置为 false 会使得每个 router-link 导航都触发整页刷新。它可用于工作在 IE9 下的服务端渲染应用,因为一个 hash 模式的 URL 并不支持服务端渲染。
——————————————————————————————————————————————————————————————————
Router 实例属性
router.app
- 类型: Vue instance(实例)
- 配置了 router 的 Vue 根实例。
router.mode
router.currentRoute
Router 实例方法
router.beforeEach
router.beforeResolve
router.afterEach
router.push
- location 该方法的参数可以是一个字符串路径,或者一个描述地址的对象
- 在 2.2.0+,可选的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。
- 在 3.1.0+,可以省略第二个和第三个参数,此时如果支持 Promise,router.push 或 router.replace 将返回一个 Promise
router.push(location, onComplete?, onAbort?)
router.push(location).then(onComplete).catch(onAbort)
router.replace
router.replace(location, onComplete?, onAbort?)
router.replace(location).then(onComplete).catch(onAbort)
router.go
- 如果 history 记录不够用,那就默默地失败呗
router.go(n)
router.back
router.forward(向前)
router.getMatchedComponents
- 返回目标位置或是当前路由匹配的组件数组 (是数组的定义/构造类,不是实例)。通常在服务端渲染的数据预加载时使用。
const matchedComponents: Array<Component> = router.getMatchedComponents(location?)
router.resolve
- 解析目标位置 (格式和 <router-link> 的 to prop 一样)。
- current 是当前默认的路由 (通常你不需要改变它)
- 注释:current是一个路由信息对象,当参数1为相对路径字符串时,相对的就是该值进行解析。当该值为空时默认相对当前路径
- append 允许你在 current 路由上附加路径 (如同 router-link)
const resolved: {
location: Location;
route: Route;
href: string;
} = router.resolve(location, current?, append?)
router.addRoutes
- 动态添加更多的路由规则。参数必须是一个符合 routes 选项要求的数组。
- 注释:可以根据后端返回的菜单接口生成路由数组,然后动态加入。用于控制路由权限,会比路由拦截更加节省性能
router.addRoutes(routes: Array<RouteConfig>)
router.onReady
- 该方法把一个回调排队,在路由完成初始导航时调用,这意味着它可以解析所有的异步进入钩子和路由初始化相关联的异步组件。
- 注释:先执行所有路由守卫,然后才执行onReady,在beforeCreate之前
- 这可以有效确保服务端渲染时服务端和客户端输出的一致。
- 第二个参数 errorCallback 只在 2.4+ 支持。它会在初始化路由解析运行出错 (比如解析一个异步组件失败) 时被调用。
- 注释:callback接收一个to路由信息对象作为参数
router.onReady(callback, [errorCallback])
router.onError
- 注册一个回调,该回调会在路由导航过程中出错时被调用。注意被调用的错误必须是下列情形中的一种
- 错误在一个路由守卫函数中被同步抛出;
- 错误在一个路由守卫函数中通过调用 next(err) 的方式异步捕获并处理;
- 渲染一个路由的过程中,需要尝试解析一个异步组件时发生错误。
router.onError(callback)
——————————————————————————————————————————————————————————————————
路由对象
- 一个路由对象 (route object) 表示当前激活的路由的状态信息,包含了当前 URL 解析得到的信息,还有 URL 匹配到的路由记录 (route records)。
- 路由对象是不可变 (immutable) 的,每次成功的导航后都会产生一个新的对象。
- 路由对象出现在多个地方
- 在组件内,即 this.$route
- 在 $route 观察者回调内,组件复用时组件的生命周期钩子不会再被调用
- router.match(location) 的返回值。注释:新版本文档中已经没有该方法了,可能被替换为 router.resolve
- 注释:router.resolve的返回值的route属性也是一个路由对象
- 导航守卫的参数
- scrollBehavior 方法的参数
路由对象属性
$route.path
- 类型: string
- 字符串,对应当前路由的路径,总是解析为绝对路径,如 "/foo/bar"
$route.params
- 类型: Object
- 一个 key/value 对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
- 注释:全匹配片段是指
*
或者(foo/)?
等方式匹配的路由
- 注释:全匹配片段通过 pathMatch 或数值 key 访问
- 注释:非具名参数匹配的字段如果只有单个,对应的key为
pathMatch
,如果有多个,那么后续key由1开始。例如:path: "/other/(foo/)?(foo2/)?bar"
中pathMatch==='foo/
'1==='foo2/'
- 注释:其实
0==='foo/'
和pathMatch是一样的
$route.query
- 类型: Object
- 一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user == 1,如果没有查询参数,则是个空对象。
- 注释:始终和url的?查询参数保持一致,即当url被stringifyQuery改变时才发生改变,和parseQuery / stringifyQuery的返回结果没有直接关系,通过name跳转页面时,stringifyQuery是不会改变url的
$route.hash
- 类型: string
- 当前路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串。
$route.fullPath
- 类型: string
- 完成解析后的 URL,包含查询参数和 hash 的完整路径。
$route.matched
- 类型: Array<RouteRecord>
- 一个数组,包含当前路由的所有嵌套路径片段的路由记录 。路由记录就是 routes 配置数组中的对象副本 (还有在 children 数组)。
- 注释:数组是因为路由可能存在嵌套,每一级都会匹配一个路由记录。
- 注释:虽然是副本,但不完全等于路由配置项
$route.name
$route.redirectedFrom
- 如果存在重定向,即为重定向来源的路由的名字。
- 注释:如果/a重定向到/b,当前页面是/b时,点击/a链接是不会跳转和重定向的,该值也将返回空。
——————————————————————————————————————————————————————————————————
组件注入
注入的属性
- 通过在 Vue 根实例的 router 配置传入 router 实例,下面这些属性成员会被注入到每个子组件。
- this.$router
- this.$route 当前激活的路由信息对象。这个属性是只读的,里面的属性是 immutable (不可变) 的,不过你可以 watch (监测变化) 它。
增加的组件配置选项
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
——————————————————————————————————————————————————————————————————
导航守卫
- 导航被触发。
- 在失活的组件里调用离开守卫。beforeRouteLeave
- 参数或查询的改变并不会触发进入/离开的导航守卫。
- 可以访问组件实例
this
- 调用全局的 beforeEach(全局前置守卫) 守卫。
router.beforeEach((to, from, next) => {...})
- 当一个导航触发时,全局前置守卫按照创建顺序调用。
- 在 2.5.0+ 这三个方法都返回一个移除已注册的守卫/钩子的函数。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 2.2 新增
- 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。可以访问组件实例
this
- 注释:初次进入不会触发,只有当前页面复用才会触发,需要对页面进行初始化建议使用 watch $route
- 在路由配置里调用 beforeEnter。
routes[0].beforeEnter = (to, from, next) => {...}
- 参数或查询的改变并不会触发进入/离开的导航守卫。
- 注释:beforeEnter无法访问 vue 实例
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
beforeRouteEnter (to, from, next) { next(vm => {...}) }
- 参数或查询的改变并不会触发进入/离开的导航守卫。
- 不!能!获取组件实例
this
- 可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
- 调用全局的 beforeResolve 守卫 (2.5+)。
router.beforeResolve((to, from, next) => {...})
- 在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
- 在 2.5.0+ 这三个方法都返回一个移除已注册的守卫/钩子的函数。
- 注释:router.beforeResolve 无法访问 vue 实例
- 导航被确认。
- 导航被确认前,用户会停留在当前的界面,如果采用在路由守卫中获取数据,建议在数据获取期间,显示一些进度条或者别的指示。
- 调用全局的 afterEach 钩子。
router.afterEach((to, from) => {...})
- 在 2.5.0+ 这三个方法都返回一个移除已注册的守卫/钩子的函数。
- 注释:router.afterEach 中没有 vue 实例
- 触发 DOM 更新。
- 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
next
- next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
- next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
- next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
- 注释:当输入url时(或点返回时),url首先改变请求新的文档,如果该文档中的路由守卫返回next(false)的话,会重置当前url到from路由对应的地址。但是页面并不能正常,应该是执行了history.go(-1),谷歌中history.go应该是不会重新执行页面中的JS的
- 注释:被 next(false) 的路由是不会写入 history 中的。
- 注释:在谷歌中点击后退,被中断的路由依然会记录目标路由。即点前进按钮可以进到目标路由地址。但是改变url却不会
- next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
- 注释:这种跳转类似于重定向,不会在history中写入跳转前地址。
- next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
以下为原文————————————————————————————————————————————————————————————————————————————————————
安装
直接下载 / CDN
NPM
npm install vue-router
- 在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
构建开发版
介绍
- 对于 TypeScript 用户来说,vue-router@3.0+ 依赖 vue@2.5+,反之亦然。
起步
HTML
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
JavaScript
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes
})
- 通过注入路由器,我们可以在任何组件内通过 this.$router 访问路由器,也可以通过 this.$route 访问当前路由
- this.$router 和 router 使用起来完全一样
- 当 <router-link> 对应的路由匹配成功,将自动设置 class 属性值 .router-link-active。
动态路由匹配
- “动态路径参数”(dynamic segment)
- 当匹配到一个路由时,参数值会被设置到 this.$route.params
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
- 可以在一个路由中设置多段“路径参数”,对应的值都会设置到 $route.params 中
/user/:username/post/:post_id /user/evan/post/123 { username: 'evan', post_id: '123' }
响应路由参数的变化
- 当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。
- 这也意味着组件的生命周期钩子不会再被调用。
- 复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象
const User = {
template: '...',
watch: {
'$route' (to, from) {
// 对路由变化作出响应...
}
}
}
- 或者使用 2.2 中引入的 beforeRouteUpdate 导航守卫
- 注释:初次进入不会触发,只有当前页面复用才会触发,需要对页面进行初始化建议使用 watch $route
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
捕获所有路由或 404 Not found 路由
{
// 会匹配所有路径
path: '*'
}
{
// 会匹配以 `/user-` 开头的任意路径
path: '/user-*'
}
- 当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后(404错误页面)
- 如果你使用了History 模式,请确保正确配置你的服务器。
- 当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分
// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'
高级匹配模式
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/' },
// params are denoted with a colon ":"
{ path: '/params/:foo/:bar' },
// a param can be made optional by adding "?" 注释:可选参数
{ path: '/optional-params/:foo?' },
// a param can be followed by a regex pattern in parens
// this route will only be matched if :id is all numbers
{ path: '/params-with-regex/:id(\d+)' }, 注释:正则验证
// asterisk can match anything
{ path: '/asterisk/*' },
// make part of the path optional by wrapping with parens and add "?" 注释:可选路径
{ path: '/optional-group/(foo/)?bar' }
]
})
匹配优先级
- 同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。
嵌套路由
- 创建vue实例时使用的根组件包含的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。
- 要在嵌套的出口中渲染组件,需要在 VueRouter 的参数中使用 children 配置
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
}
]
}
]
})
- 要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。
- 注释:当children中的path使用/开头的根路径时,通过该路径直接访问到这个嵌套组件(父及祖组件都会被渲染)。简而言之,组件时嵌套的,但路由不是。
- 基于上面的配置,当你访问 /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(location, onComplete?, onAbort?)
- 当你点击 <router-link> 时,这个方法会在内部调用
- 该方法的参数可以是一个字符串路径,或者一个描述地址的对象
- 注释:同一个router实例中不允许出现相同的name,即使他们在不同的嵌套层级,控制台会出现提示
- 注释:通过父级命名路由跳转,不会匹配
children
中path:""
指向的组件
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
- 如果提供了 path,params 会被忽略
- 同样的规则也适用于 router-link 组件的 to 属性
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
- 在 2.2.0+,可选的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。
- 在 3.1.0+,可以省略第二个和第三个参数,此时如果支持 Promise,router.push 或 router.replace 将返回一个 Promise
router.replace(location, onComplete?, onAbort?)
<router-link :to="..." replace> router.replace(...)
router.go(n)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
操作 History
- Vue Router 的导航方法 (push、 replace、 go) 在各类路由模式 (history、 hash 和 abstract) 下表现一致
命名路由
命名视图
- 同时 (同级) 展示多个视图
- 如果 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>
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
嵌套命名视图
<!-- 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
}
}]
}
重定向和别名
重定向
- 下面例子是从 /redirect-with-params/:id 重定向到 /with-params/:id
- 注释:重定向后会显示定向后的url,router-link-active也被添加到定向后的router-link上
- 注释:重定向后的参数可以是定值,例如:
{ path: '/redirect-with-params/:id', redirect: '/with-params/456' }
这个时候params.id === '456'
- 注释:重定向前后的path段不需要完全匹配,例如:
{ path: '/redirect-with-params/other-path/:id', redirect: '/with-params/:id' }
这个时候id依然能够正常传递
- 注释:重定向的参数依据定向前参数名传递,而不是依据定向目标url参数名。例如目标 path 为
/with-params/:id
当前route可写为{ path: '/redirect-with-params/:other-id', redirect: '/with-params/:other-id' }
- 注释:重定向前的url不会写入history中?
- 注释:如果当前路由是目标路由重定向后的路由,该跳转是不会被执行的,也不存在又一次重定向。
const router = new VueRouter({
routes: [
{ path: '/redirect-with-params/:id', redirect: '/with-params/:id' }
]
})
- 重定向的目标也可以是一个命名的路由
- 注释:同字符串,
params.id
可以设置为固定值
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
- 甚至是一个方法,动态返回重定向目标
- 注释:返回的值等价于字符串或对象即可,不需要生成含有确认参数的路径。例如:
return '/with-params/:id'
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
- 注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 /a 路由添加一个 beforeEach 或 beforeLeave 守卫并不会有任何效果
- 高级用法
// redirect with caseSensitive 区分大小写
// 注释:谷歌输入URL会自动转为小写
// 注释:在vue中通过path跳转,可以使用大写。但通过地址栏输入不行,建议不开启该功能
{ path: '/foobar', component: Foobar, caseSensitive: true }
// redirect with pathToRegexpOptions 编译正则的选项
{ path: '/FooBar', component: FooBar, pathToRegexpOptions: { sensitive: true }},
别名
- /a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
- “别名”的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。
- 疑问:似乎children中的path由/开头也有相同效果
- 高级用法
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/root', component: Root, alias: '/root-alias' },
{ 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'] },
// default child route with empty string as alias. 空字符串别名
{ path: 'default', component: Default, alias: '' },
// nested alias 嵌套别名,/home/nested-alias/foo 等同于访问 /home/nested/foo
{ path: 'nested', component: Nested, alias: 'nested-alias',
children: [
{ path: 'foo', component: NestedFoo }
]
}
]
}
]
})
路由组件传参
- 在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用
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 是一个对象,它会被按原样设置为组件属性
- 注释:这里是指和component配合使用时设置成对象,等于向组件props传递了一个固定值。例如:
props:{id:10}
函数模式
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
- 请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用
HTML5 History 模式
后端配置例子
Apache(Web服务器软件,略)
nginx(Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,反向代理作为Web服务器的前置机来降低网络和服务器的负载,略)
原生 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
Caddy(类似 Nginx,略)
Firebase 主机(谷歌开发APP的框架,略)
警告
- 使用 Node.js 服务器,你可以用服务端路由匹配到来的 URL,并在没有匹配到路由的时候返回 404,以实现回退。
导航守卫
- 有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的
- 参数或查询的改变并不会触发进入/离开的导航守卫。
全局前置守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
- 当一个导航触发时,全局前置守卫按照创建顺序调用。
- 注释:多次调用 router.beforeEach() 可以注册多个全局前置守卫
- 每个守卫方法接收三个参数:
- to: Route: 即将要进入的目标 路由对象
- from: Route: 当前导航正要离开的路由
- next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
- next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
- next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
- 注释:当输入url时(或点返回时),url首先改变请求新的文档,如果该文档中的路由守卫返回next(false)的话,会重置当前url到from路由对应的地址。但是页面并不能正常,应该是执行了history.go(-1),谷歌中history.go应该是不会重新执行页面中的JS的
- 注释:被 next(false) 的路由是不会写入 history 中的。
- 注释:在谷歌中点击后退,被中断的路由依然会记录目标路由。即点前进按钮可以进到目标路由地址。但是改变url却不会
- next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
- 注释:这种跳转类似于重定向,不会在history中写入跳转前地址。
- next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
全局解析守卫
- 2.5.0 新增
- router.beforeResolve 在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
- 注释:router.beforeResolve 无法访问 vue 实例
全局后置钩子
- 你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身
- 注释:router.afterEach 中没有 vue 实例
router.afterEach((to, from) => {
// ...
})
路由独享的守卫
- 注释:beforeEnter无法访问 vue 实例
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) {
// 2.2 新增
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
- beforeRouteEnter 可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
- 注释:当前路由改变,但是该组件被复用时不会触发
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用离开守卫。beforeRouteLeave
- 调用全局的 beforeEach(全局前置守卫) 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。在 mounted 之后
路由元信息
- routes 配置中的每个路由对象为 路由记录
- 当一个路由匹配成功后,他可能匹配多个路由记录
- 注释:所有级别路由都可以配置元信息 meta,不管是根级路由还是末级路由
- 一个路由匹配到的所有路由记录会暴露为 $route 对象 (还有在导航守卫中的路由对象) 的 $route.matched 数组。
- 遍历 $route.matched 来检查路由记录中的 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()
}
})
过渡动效
- <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'
}
}
数据获取
导航完成后获取数据
在导航完成前获取数据
- 在为后面的视图获取数据时,用户会停留在当前的界面,因此建议在数据获取期间,显示一些进度条或者别的指示。
滚动行为
- 这个功能只在支持 history.pushState 的浏览器中可用
- 第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。
- 这个方法返回滚动位置的对象信息,长这样:
- 可以利用路由元信息更细颗粒度地控制滚动。
- 注释:scrollBehavior只有在路由切换时才触发,通过url进入页面不会触发
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// savedPosition
return savedPosition
// 滚动到锚点
return {
selector: to.hash
}
// 偏离
return {
offset: { y: 100 }
}
}
})
异步滚动
- 2.8.0 新增
- 可以返回一个 Promise 来得出预期的位置描述
- 注释:可以等待动画结束之后才触发滚动
- 注释:例子中返回的 Promise 中采用 $once 在根组件上绑定事件,而不是使用 $on ,是因为每次 scrollBehavior 执行后都返回一个新的 Promise,根组件触发 triggerScroll 事件后需要 resolve 这个新的 Promise
const scrollBehavior = function (to, from, savedPosition) {
return new Promise(resolve => {
const position = {...}
// wait for the out transition to complete (if necessary)
this.app.$root.$once('triggerScroll', () => {
// if the resolved position is falsy or an empty object,
// will retain current scroll position.
resolve(position)
})
})
}
}
const router = new VueRouter({
mode: 'history',
base: __dirname,
scrollBehavior,
routes: [
...
]
})
new Vue({
router,
template: `
<div id="app">
<ul>
<li><router-link to="/">/</router-link></li>
<li><router-link to="/foo">/foo</router-link></li>
</ul>
<transition name="fade" mode="out-in" @after-leave="afterLeave">
<router-view class="view"></router-view>
</transition>
</div>
`,
methods: {
afterLeave () {
this.$root.$emit('triggerScroll')
}
}
}).$mount('#app')
路由懒加载
- 可以将异步组件定义为返回一个 Promise 的工厂函数 (该函数返回的 Promise 应该 resolve 组件本身)
const Foo = () => Promise.resolve({ /* 组件定义对象 */ })
- 使用动态 import 语法来定义代码分块点 (split point):
- 如果您使用的是 Babel,你将需要添加 syntax-dynamic-import 插件,才能使 Babel 可以正确地解析语法。
- 注释:Babel 把import解析为返回 Promise 的函数,Webpack 使用该标志来进行代码分块,注释语句也是有 Webpack 提供。
import('./Foo.vue') // 返回 Promise
- 结合这两者,这就是如何定义一个能够被 Webpack 自动代码分割的异步组件
const Foo = () => import('./Foo.vue')
把组件按组分块
- 把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
————————————————————————————————————————————————————————————————————————————————————
异步组件
- Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。
- 这个工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器得到组件定义的时候被调用
- 可以调用 reject(reason) 来表示加载失败。
- 如何获取组件取决于你自己。一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用:https://webpack.docschina.org/guides/code-splitting/
Vue.component('async-webpack-example', function (resolve, reject) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
// 向 `resolve` 回调传递组件定义
// 实际最后回调
resolve({
template: '<div>I am async!</div>'
})
})
- 也可以在工厂函数中返回一个 Promise,所以把 webpack 2 和 ES2015 语法加在一起,我们可以写成这样:
Vue.component(
'async-webpack-example',
// 这个 `import` 函数会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
处理加载状态
- 2.3.0+ 新增
- 这里的异步组件工厂函数也可以返回一个如下格式的对象:
- 如果你希望在 Vue Router 的路由组件中使用上述语法的话,你必须使用 Vue Router 2.4.0+ 版本。
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
————————————————————————————————————————————————————————————————————————————————————
- Turn an Express-style path string such as /user/:name into a regular expression.转变Express-style路径为正则表达式
Installation
npm install path-to-regexp --save
Usage
var pathToRegexp = require('path-to-regexp')
// pathToRegexp(path, keys, options)
// pathToRegexp.parse(path)
// pathToRegexp.compile(path)
- path: An Express-style string, an array of strings, or a regular expression.Express-style类字符串、字符串数组、正则表达式
- keys: An array to be populated with the keys found in the path.用来存放路径中变量对象的数组容器,解析path后会往这个数组塞解析好的变量对象
- options:
- 注释:vue-router中的 pathToRegexpOptions 对象,应该和这的options相同
- sensitive: When true the route will be case sensitive. (default: false) true时区分大小写
- strict: When false the trailing slash is optional. (default: false) false时尾部斜杠在正则中为可有可无
- end: When false the path will match at the beginning. (default: true) false时正则将拥有,疑问:好像true才是拥有
- delimiter: Set the default delimiter for repeat parameters. (default: '/') 设置默认间隔符号
var keys = []
var re = pathToRegexp('/foo/:bar', keys)
// re = /^/foo/([^/]+?)/?$/i
// keys = [{
name: 'bar',
prefix: '/', // 前缀
delimiter: '/', // 分隔符
optional: false, // 是否可选
repeat: false, // 是否多个,用于匹配*,例如:/:foo*
pattern: '[^\/]+?' // 正则字符串
partial Indicates this token is a partial path segment (boolean) // 是否为部分路径?
asterisk Indicates the token is an * match (boolean) // 是否为*匹配
}]
- Please note: The RegExp returned by path-to-regexp is intended for use with pathnames or hostnames. It can not handle the query strings or fragments of a URL.返回的正则只能处理路径或主机名,无法处理查询字符串?和片段#。应该是生成的正则直接就舍去了这部分
Parameters 参数
Named Parameters 命名参数
var re = pathToRegexp('/:foo/:bar', keys)
// keys = [{ name: 'foo', prefix: '/', ... }, { name: 'bar', prefix: '/', ... }]
re.exec('/test/route')
//=> ['/test/route', 'test', 'route']
- Please note: Named parameters must be made up of "word characters" ([A-Za-z0-9_]). 命名参数需要满足 [A-Za-z0-9_]
- 注释:命名参数可以和常量、其他参数类型混搭
- 注释:命名参数在路径变量keys中name为命名值,而未命名的参数的name是数字
var re = pathToRegexp('/(apple-)?icon-:res(\d+).png', keys)
// keys = [{ name: 0, prefix: '/', ... }, { name: 'res', prefix: '', ... }]
re.exec('/icon-76.png')
//=> ['/icon-76.png', undefined, '76']
Modified Parameters 变动参数
Optional 可选的?
Zero or more 0个或更多
var re = pathToRegexp('/:foo*', keys)
// keys = [{ name: 'foo', delimiter: '/', optional: true, repeat: true }]
re.exec('/')
//=> ['/', undefined]
re.exec('/bar/baz')
//=> ['/bar/baz', 'bar/baz']
One or more 1个或更多 +
var re = pathToRegexp('/:foo+', keys)
// keys = [{ name: 'foo', delimiter: '/', optional: false, repeat: true }]
re.exec('/')
//=> null
re.exec('/bar/baz')
//=> ['/bar/baz', 'bar/baz']
Custom Match Parameters 自定义匹配参数
- All parameters can be provided a custom regexp, which overrides the default ([^/]+). 参数可以提供一个正则进行校验,替代默认的 [^/]+
Unnamed Parameters 未命名参数
- It is possible to write an unnamed parameter that only consists of a matching group. It works the same as a named parameter, except it will be numerically indexed.
- 注释:可以只写正则匹配规则
var re = pathToRegexp('/:foo/(.*)', keys)
// keys = [{ name: 'foo', ... }, { name: 0, ... }]
re.exec('/test/route')
//=> ['/test/route', 'test', 'route']
Asterisk
- An asterisk can be used for matching everything. It is equivalent to an unnamed matching group of (.*).
- 注释:可以匹配所有内容,相当于一个 (.) 匹配组。这里的 * 代表含义不同,前者为通配符,后者为正则0个或n个
Parse 解析
var tokens = pathToRegexp.parse('/route/:foo/(.*)')
console.log(tokens[0])
//=> "/route"
console.log(tokens[1])
//=> { name: 'foo', prefix: '/', delimiter: '/', optional: false, repeat: false, pattern: '[^\/]+?' }
console.log(tokens[2])
//=> { name: 0, prefix: '/', delimiter: '/', optional: false, repeat: false, pattern: '.*' }
Compile ("Reverse" Path-To-RegExp) 编译
var toPath = pathToRegexp.compile('/user/:id')
toPath({ id: 123 }) //=> "/user/123"
toPath({ id: 'café' }) //=> "/user/caf%C3%A9"
toPath({ id: '/' }) //=> "/user/%2F"
toPath({ id: ':' }) //=> "/user/%3A"
toPath({ id: ':' }, { pretty: true }) //=> "/user/:"
var toPathRepeated = pathToRegexp.compile('/:segment+')
toPathRepeated({ segment: 'foo' }) //=> "/foo"
toPathRepeated({ segment: ['a', 'b', 'c'] }) //=> "/a/b/c"
var toPathRegexp = pathToRegexp.compile('/user/:id(\d+)')
toPathRegexp({ id: 123 }) //=> "/user/123"
toPathRegexp({ id: '123' }) //=> "/user/123"
toPathRegexp({ id: 'abc' }) //=> Throws `TypeError`.
Working with Tokens
- pathToRegexp.tokensToRegExp(tokens, options) Transform an array of tokens into a matching regular expression. 将 tokens 转化为正则表达式
- 注释:tokens 来源于
var tokens = pathToRegexp.parse('/route/:foo/(.*)')
- pathToRegexp.tokensToFunction(tokens) Transform an array of tokens into a path generator function. 将 tokens 转化为路径生成函数
- 注释:应该等价于
var toPath = pathToRegexp.compile('/user/:id')
Compatibility with Express <= 4.x 于Express兼容
TypeScript