vue-router
路由作用
背景:传统开发模式中,每个页面对应着唯一的url,如
www.XXX.com/home
对应着home页,此类多页应用在切换页面时,整个页面需要整体刷新重绘,极大影响性能。因此,SPA单页面应用模式出现,极大地提高了性能;
SPA:单页面应用,其主要的支撑技术包括:1,ajax实现了局部数据刷新;2,路由跳转不请求页面情况下实现页面的更新;
扩展
- 什么是重绘?
重绘:当元素外观,样式发生改变,但是不影响布局,则称为重绘(如backgorund
颜色改变等),即重新将改变后的元素绘制到屏幕上,但是该操作本身不影响文档流的结构; - 什么是回流?
回流:renderTree中的元素因为规模尺寸,隐藏,布局等改变,导致整个结构重新调整(比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变),则称为回流,因此回流必定重绘,重绘不一定回流
详细见:13.重绘和回流详解
Vue中路由
- 作用
1. 监听url变化,并在变化前后执行相应的逻辑
2. 不同的url对应着不同的组件;
3. 提供多种路由API进行局部刷新前后的操作;
基本路由
- 初始化路由器实例对象,
new VueRouter()
new VueRouter({
// 路由配置项
})
- 配置路由列表,每个路由对应一个组件
routes: [{
// 一般路由
path: '/about',
component: AboutComponent,
}, {
// 可以设置默认跳转的页面
path: '/', // '/'表示根路径
component: AboutComponent,// 默认跳转到about对应的页面
}]
- 注册路由器,将路由对象实例挂载到vue对象实例中;
import router from './routes'
new Vue(
{
router
})
- 使用路由组件
//<router-link> 用于触发跳转,指定跳转的页面
<router-link to="/about">点击跳转到about页面</router-link>
//<router-view> 用于显示路由跳转过来的组件
<router-view></router-view>
详细见:官网Vue-router
- 定义/配置/注册路由
// 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/route使用路由
<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>
动态路由
const User = {
template: '<div>User</div>'
}
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
现在像 /user/foo 和 /user/bar 都将映射到相同的路由
嵌套路由
- 嵌套路由就是在基本路由上可以下钻,路由对象中有children属性,可以配置下级路由;
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
}
]
}
]
})
1.路由以 / 开头的嵌套路径会被当作根路径
2.如果子路由中path
没有配置/
则表示当前路径可以直接拼接在父路由上
编程式导航
// 在浏览器记录中前进一步,等同于 history.forward()
this.$router.go(1)
// 后退一步记录,等同于 history.back()
this.$router.go(-1)
// 相当于点击<router-link>,跳转至path页面
this.$router.push(path)
// 新路由替代当前路由,回退不再显示当前路由页面
this.$router.replace(path)
// 请求返回上一页面
this.$router.back()
路由传参
this.$route.param.id
方式获取参数
- 通过动态路由传递参数,此处的id值,通过
this.$route.param.id
获取;
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
- 通过路由视图传参 ,同样可以通过
this.$route.param.id
获取;
<router-view :msg="message"><router-view>
不足之处:在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
props
方式获取参数
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
// 路由props传递布尔值
{ path: '/user/:id', component: User, props: true },
// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
},
// props传递对象
{
path: '/promotion/from-newsletter',
component: Promotion,
props: { newsletterPopup: false }
},
// props函数式传参
{
path: '/search',
component: SearchUser,
props: route => ({ query: route.query.q })
}
]
})
- props传布尔值参数,如果 props 被设置为 true,route.params 将会被设置为组件属性;
- props可以传递对象或者通过函数式传递参数;
注意当动态传参时,对于包含命名视图的路由,你必须分别为每个命名视图添加 props 选项:
命名视图
- 一个
<router-view>
表示一个视图,如果想多个同级而非嵌套视图一起展示,则需要多个视图,比如CMS系统中侧边栏和内容栏部分;此时需要给不同的视图命名以作区分,不命名则默认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>
缓存路由组件
背景:默认情况下,路由切换后之前的路由组件对象会被内存会被释放,再次切回时会重新创建路由组件,因此先前数据会被刷新【例子,两个tab页,切换之后,先前tab对应的数据会被清除】,如果想数据可以保留;可以使用<keep-alive>
组件将需要混存的视图包裹起来,即:
<keep-alive>
<router-view></router-view>
</keep-alive>
重定向和别名
- 重定向:就是将原本路由指定到另一个路由上;
const router = new VueRouter({
routes: [
// 路由路径
{ path: '/a', redirect: '/b' },
// 命名路由
{ path: '/bar', redirect: { name: 'foo' }},
// 函数返回重定向路由
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
]
})
- 别名:
/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。
意思是,/b就是一个别名,但是本质上还是访问/a,触发二者效果一样,别名作用在于可以使用任意URL当成/a
使用,而不需要考虑嵌套的路由结构;
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
路由懒加载
路由中映射的组件,可以异步导入,如此,可以实现代码的组件化分割,当被访问时加载对应的组件,提高效率。【注意:需要vue2.4以上,需要用到异步组件】
- 结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。
const Foo = () =>
Promise.resolve({
/* 组件定义对象 */
})
- 在 Webpack 2 中,我们可以使用动态 import语法来定义代码分块点 (split point)
import('./Foo.vue') // 返回 Promise
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [{ path: '/foo', component: Foo }]
})
路由原理
SPA中路由变化时,会更新视图但是不会请求页面;这是路由的核心原理之一;其实现方法包括:
- hash路由,利用URL中的hash("#");
- 利用History interface在HTML5中新增的方法;
二者对比:
- pushState设置的新url可以是与当前url同源的任意url,而hash只可修改#后面的部分,故只可设置与当前同文档的url
- pushState设置的新url可以与当前url一模一样,这样也会把记录添加到栈中,而hash设置的
- 新值必须与原来不一样才会触发记录添加到栈中
- pushState通过stateObject可以添加任意类型的数据记录中,而hash只可添加短字符串
- pushState可额外设置title属性供后续使用