Vue Router汇总(摘自官网)
Vue Router汇总(摘自官网)
1.vue-router功能介绍
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于Vue.js过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的CSS class的链接
- HTML5历史模式或hash模式,在IE9中自动降级
- 自定义的滚动条行为
- 路由模式三种(hash,history,abstract)
2.动态路由配置
常规参数:
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个
User组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在vue-router的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:
const User = {
template: '<div>User</div>'
}
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
| 模式 | 匹配路径 | $route.params |
|---|---|---|
| /user/:username | /user/evan | { username: 'evan' } |
| /user/:username/post/:post_id | /user/evan/post/123 | { username: 'evan', post_id: '123' } |
注意:
当使用路由参数时,例如从
/user/foo导航到/user/bar, 原来的组件实例会被复用 。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。 不过,这也意味着组件的生命周期钩子不会再被调用 。
可以使用watch进行路由监听
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()
}
}
非常规参数
常规参数只会匹配被
/分隔的 URL 片段中的字符。如果想匹配 任意路径 ,我们可以使用通配符 (*):
{
// 会匹配所有路径
path: '*'
}
{
// 会匹配以 `/user-` 开头的任意路径
path: '/user-*'
}
3.嵌套路由
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
}
]
}
]
})
注意:
以
/开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。 此时,基于上面的配置,当你访问/user/foo时,User的出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个 空的 子路由:
const router = new VueRouter({
routes: [
{
path: '/user/:id', component: User,
children: [
// 当 /user/:id 匹配成功,
// UserHome 会被渲染在 User 的 <router-view> 中
{ path: '', component: UserHome },
// ...其他子路由
]
}
]
})
4.编程式导航
1). router.push(location, onComplete?, onAbort?)
当你点击
<router-link>时,这个方法会在内部调用,所以说,点击<router-link :to="...">等同于调用router.push(...)。
| 声明式 | 编程式 |
|---|---|
<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' }})
注意:
如果提供了
path,params会被忽略 注意 : 如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个/users/1->/users/2),你需要使用beforeRouteUpdate来响应这个变化 (比如抓取用户信息)。
2). router.replace(location, onComplete?, onAbort?)
跟
router.push很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
| 声明式 | 编程式 |
|---|---|
<router-link :to="..." replace> |
router.replace(...) |
3). router.go(n)
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似
window.history.go(n)。
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
小结:
你也许注意到
router.push、router.replace和router.go跟window.history.pushState、window.history.replaceState和window.history.go好像, 实际上它们确实是效仿window.historyAPI 的。
5.命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有
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>
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
注意:
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用**
components**配置 (带上 s) :
6.HTML5 History 模式
history模式需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问就会返回 404,这就不好看了。要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个
index.html页面
后台配置例子:
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>
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)
})
警告
给个警告,因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回
index.html文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '*', component: NotFoundComponent }
]
})
7.导航守卫
记住 参数或查询的改变并不会触发进入/离开的导航守卫 。你可以通过 观察
$route对象 来应对这些变化,或使用beforeRouteUpdate的组件内守卫。
-
全局前置守卫
beforeEach
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
// 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是**confirmed**(确认的)。
next();
// 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到`from`路由对应的地址。
next(false):
// 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向`next`传递任意位置对象,且允许设置诸如`replace: true`、`name: 'home'`之类的选项以及任何用在[`router-link`的`to`prop](https://router.vuejs.org/zh/api/#to)或[`router.push`](https://router.vuejs.org/zh/api/#router-push)中的选项。
next('/')`或者`next({ path: '/' })
// 如果传入`next`的参数是一个`Error`实例,则导航会被终止且该错误会被传递给[`router.onError()`](https://router.vuejs.org/zh/api/#router-onerror)注册过的回调。
next(error)
})
-
全局解析守卫
beforeResolve
在 2.5.0+ 你可以用
router.beforeResolve注册一个全局守卫。这和router.beforeEach类似,区别是在导航被确认之前, 同时在所有组件内守卫和异步路由组件被解析之后 ,解析守卫就被调用。
const router = new VueRouter({ ... })
router.beforeResolve((to, from, next) => {
// ...
next();
})
-
全局后置钩子
afterEach
const router = new VueRouter({ ... })
// 后置钩子不接受next参数
router.afterEach((to, from) => {
// ...
})
-
路由独享的守卫
beforeEnter
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
-
组件内的守卫
beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave
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`
}
}
8. 完整的导航解析流程
1. 导航被触发。
2. 在失活的组件里调用离开守卫。
3. 调用全局的`beforeEach`守卫。
4. 在重用的组件里调用`beforeRouteUpdate`守卫 (2.2+)。
5. 在路由配置里调用`beforeEnter`。
6. 解析异步路由组件。
7. 在被激活的组件里调用`beforeRouteEnter`。
8. 调用全局的`beforeResolve`守卫 (2.5+)。
9. 导航被确认。
10. 调用全局的`afterEach`钩子。
11. 触发 DOM 更新。
12. 用创建好的实例调用`beforeRouteEnter`守卫中传给`next`的回调函数。
9.过渡动效
<transition name="fade">
<router-view></router-view>
</transition>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
10.数据获取
-
导航完成后获取数据
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) => {
next(vm => vm.setData(err, post))
})
},
// 路由改变前,组件就已经渲染完了
// 逻辑稍稍不同
beforeRouteUpdate (to, from, next) {
this.post = null
getPost(to.params.id, (err, post) => {
this.setData(err, post)
next()
})
},
methods: {
setData (err, post) {
if (err) {
this.error = err.toString()
} else {
this.post = post
}
}
}
}
11.滚动行为
注意: 这个功能只在支持
history.pushState的浏览器中可用。
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
// 在按下 后退/前进 按钮时,就会像浏览器的原生表现那样
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
// 或启用锚点
if (to.hash) {
return {
selector: to.hash
}
}
}
})
12.路由懒加载
注意: 如果您使用的是 Babel,你将需要添加
syntax-dynamic-import插件,才能使 Babel 可以正确地解析语法。
结合 Vue 的 异步组件 和 Webpack 的 代码分割功能 ,才实现了路由懒加载
1)异步组件定义为返回一个 Promise 的工厂函数 (该函数返回的 Promise 应该 resolve 组件本身):
const Foo = () => Promise.resolve({ /* 组件定义对象 */ })
2)在 Webpack 2 中,我们可以使用 动态 import 语法来定义代码分块点 (split point):
import('./Foo.vue') // 返回 Promise
将1)和2)合并在一起,实现了路由懒加载,如下:
const Foo = () => import('./Foo.vue')
更多推荐
所有评论(0)