Vue Router 是 vue.js 的官方插件,用来快速实现单页应用。
单页应用
SPA(Single Page Application)单页面应用程序,简称单页应用。
单页,指的是网站的 “所有” 功能都在单个页面中进行呈现。
具有代表性的有后台管理系统、移动端、小程序等。
优点:
- 前后端分离开发,提高了开发效率。
- 业务场景切换时,局部更新结构。
- 用户体验好,更加接近本地应用。
缺点:
- 不利于 SEO。
- 初次首屏加载速度较慢。
- 页面复杂度比较高。
前端路由
前端路由,指的是 URL 与内容间的映射关系。
设置前端路由必备的条件就是:URL、内容、映射关系。
比如上面的单页每个内容对应一个URL的映射关系就是前端路由。
设置前端路由的两种方式:
- Hash方式
- History方式
Hash方式
通过 hashchange 事件监听 hash 变化,并进行网页内容更新,就是设置前端路由的Hash方式。
Hash 是 URL 的组成部分,代表符号是#
,一般用作锚点在页面内的跳转,当你在看本文时,点击文章目录的各个部分,你会发现地址栏中的 URL 是变化的。这样不会引起整个页面跳转的特性,很适合用作前端路由。
<div>
<a href="#/">首页</a>
<a href="#/category">分类页</a>
<a href="#/user">用户页</a>
</div>
<div id="container">
这是首页功能
</div>
<script>
// 准备对象,用于封装 hash 功能。
var router = {
// 路由存储位置: 保存了 url 与 内容处理函数的对应关系
routes: {},
// 定义路由规则的方法
route: function (path, callback) {
this.routes[path] = callback;
},
// 初始化路由的方法
init: function () {
var that = this;
window.onhashchange = function () {
// 当 hash 改变,我们需要得到当前新的 hash
var hash = location.hash.replace('#', '');
// 根据 hash 触发 routes 中的对应 callback
that.routes[hash] && that.routes[hash]();
};
}
};
var container = document.getElementById('container');
// 定义路由规则
router.route('/', function () {
container.innerHTML = '这是首页功能';
});
router.route('/category', function () {
container.innerHTML = '这是分类功能';
});
router.route('/user', function () {
container.innerHTML = '这是用户功能';
});
// 初始化路由
router.init();
</script>
特点总结:
- Hash 方式兼容性好
- 地址中具有
#
符号,不太美观 - 前进后退功能较为繁琐
要解决地址不美观、前进后退的问题,则可使用前端路由的另一种方式,History方式。
History方式
History 方式采用 HTML5 提供的新功能实现前端路由。
在操作时需要通过 history.pushState() 变更 URL并执行对应操作。
前进后退功能,首先需要在更改 url 时保存路由标记。通过 popstate 事件监听前进后退按钮操作,并检测 state。调用初始化方法监听前进后退操作并处理。
<div>
<a href="/">首页</a>
<a href="/category">分类</a>
<a href="/user">用户</a>
</div>
<div id="container">
这是首页功能
</div>
<script>
var router = {
// 存储路由的对象
routes: {},
// 定义路由的方法
route (path, callback) {
this.routes[path] = callback;
},
// 用于触发指定的路由操作
go (path) {
// 更改 url
history.pushState({ path: path }, null, path);
// 触发路由对应的回调函数
this.routes[path] && this.routes[path]();
},
// 设置初始化方法,用来检测前进后退按钮的功能
init () {
var that = this;
window.addEventListener('popstate', function (e) {
var path = e.state ? e.state.path : '/';
that.routes[path] && that.routes[path]();
});
}
};
router.init();
// 设置 a 标签的功能
var links = document.querySelectorAll('a');
var container = document.querySelector('#container');
links.forEach(function (ele) {
ele.addEventListener('click', function (event) {
router.go(this.getAttribute('href'));
event.preventDefault();
});
});
// 路由规则
router.route('/', function () {
container.innerHTML = '首页功能';
});
router.route('/category', function () {
container.innerHTML = '分类功能';
});
router.route('/user', function () {
container.innerHTML = '用户功能';
});
</script>
优点:
- 地址格式比hash方法美观
- 实现前进后退比较方便 缺点:
- 因为是HTML5功能,兼容性稍差
- 刷新时需要后端配合处理,防止刷新访问不到路径
Vue Router使用
Vue Router是 Vue.js 官方的路由管理器,让构建单页面应用变得易如反掌。
基本使用
安装
方式一、直接下载 / CDN
- 最新版本:
https://unpkg.com/vue-router/dist/vue-router.js
- 指定版本:
https://unpkg.com/vue-router@3.4.9/dist/vue-router.js
方式二、npm 方式
npm install vue-router
引入
<!-- 因为vue-router.js是vue.js的插件,所以必须先引入vue.js -->
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
使用
<div id="app">
<!-- 设置用于进行路由操作的组件 -->
<router-link to="/">首页</router-link>
<router-link to="/user">用户</router-link>
<router-link to="/category">分类</router-link>
<!-- 路由显示区域 -->
<router-view></router-view>
</div>
<!-- 因为vue-router.js是vue.js的插件,所以必须先引入vue.js -->
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// 定义组件
var Index = {
template: `<div>首页功能</div>`
};
var User = {
template: `<div>用户功能</div>`
};
var Category = {
template: `<div>分类功能</div>`
};
// 定义路由规则
var routes = [
{ path: '/', component: Index },
{ path: '/user', component: User },
{ path: '/category', component: Category }
];
// 创建 Vue Router 实例
var router = new VueRouter({
routes
});
// 创建 Vue 实例,注入 router
var vm = new Vue({
el: '#app',
// router
});
</script>
命名视图
如果导航后,希望同时在同级展示多个视图(组件),这时就需要进行命名视图。
<div id="app">
<router-link to="/">首页</router-link>
<router-link to="/user">用户</router-link>
<router-view name="sidebar"></router-view>
<!-- 没有设置 name 的 router-view 默认 name 为 default-->
<router-view></router-view>
</div>
需要在路由规则中进行设置
// 定义路由规则
var routes = [
{
path: '/',
components: {
// router-view 的 name : 组件配置对象
default: Index,
sidebar: SideBar1
}
},
{
path: '/user',
components: {
default: User,
sidebar: SideBar2
}
}
];
动态路由
当我们需要将某一类 URL 都映射到同一个组件,就需要使用动态路由。
定义路由规则时,将路径中的某个部分使用 :
进行标记,即可设置为动态路由。
// 设置路由规则
var routes = [
{
// 表示'/user/1','/user/2','/user/3'等都指向该路由
path: '/user/:id', component: User
}
];
:
部分对应的信息称为路径参数,存储在 vm.$route.params 中。
// 设置组件
var User = {
template: `<div>这是用户 {{ $route.params.id }} 的功能</div>`
};
侦听路由参数
如果要响应路由的参数变化,可以通过 watch 监听 $route。
// 设置组件
var User = {
template: `
<div>
这是用户 {{ $route.params.id }} 的功能
<input type="text">
</div>`,
watch: {
$route (route) {
// console.log(route);
console.log(route.params.id)
}
}
};
路由传参
数据可以通过 $route.params
进行接收,但这样会导致组件和数据高度耦合,如果以后组件复用在其他位置、使用父组件或其他组件传递数据,就不好处理。
这时我们需要通过路由的 props
设置数据,并通过组件的 props
接收数据。
// 设置路由规则
var routes = [
{
path: '/user/:id',
component: User
},
{
path: '/category/:id'
component: Category,
props: true
}
];
定义组件
var User = {
template: `<div>这是用户 {{ $route.params.id }} 功能</div>`
};
var Category = {
props: ['id'],
template: `<div>这是分类 {{ id }} 功能</div>`
};
当包含多个命名视图时,需要将路由的 props 设置为对象。
// 设置路由规则
var routes = [
{
path: '/user/:id',
component: User
},
{
path: '/category/:id',
components: {
default: Category,
sidebar: SideBar,
sidebar2: SideBar2
},
props: {
default: true,
sidebar: false,
sidebar2: {
// 如果希望设置静态数据,可将 props 中的某个组件对应的选项设置为对象,
// 内部属性会绑定给组件的 props。
a: '状态1',
b: '状态2'
}
}
}
];
嵌套路由
实际场景中,路由通常由多层嵌套的组件组合而成,这时需要使用嵌套路由配置。
方法是使用 children 来进行嵌套路由中的子路由设置。
var routes = [
{
path: '/user',
component: User,
children: [
{ // 表明/user/hobby,path属性值不需要加斜杠/
path: 'hobby',
component: UserHobby
},
{ // 表明/user/info
path: 'info',
component: UserInfo,
children: [
{ // 表明/user/info/school
path: 'school',
component: UserInfoSchool
},
]
}
]
}
]
编程式导航
编程式导航,指的是通过方法设置导航。
router.push()
用来导航到一个新 URL。
vm.$router.push('/user')
// 对象结构参数
vm.$router.push({path: '/user'})
vm.$router.push({path: '/user/123'})
<router-link>
的 to
属性使用绑定方式时也可使用属性对象结构。Vue在解析的时候会调用router.push()
来处理。
<router-link :to="{ path: '/user/700' }">用户700</router-link>
命名路由
通过设置路由时添加 name
属性,来使我们更方便地对路由进行处理。
var routes = [
{
path: '/user/:id/info/school',
name: 'school',
component: School
}
];
在 router.push()
中通过 name
导航到对应路由,参数通过 params 设置。
vm.$router.push({ name: 'school', params: { id: 10 } })
也可以在 <router-link>
中使用。
<router-link :to="{ name: 'school', params: { id: 10 } }">学校10</router-link>
注意,path 方式和 name 方式不能混用。
重定向
使用 redirect 属性可以对路由进行重定向,用于防止意料之外的情况。
var routes = [
{
path: '/',
component: Index
},
{
path: '/category/:id',
component: Category
},
// 对不合理的路由访问,进行redirect重定向到根目录
{
path: '/category',
redirect: '/'
}
];
别名
别名是一种路径美化的方式。使用 alias
属性对路径进行美化,使用户看到的地址栏路径简短美观。
var routes = [
{
path: '/user/:id/info/school/:date',
name: 'school',
component: School,
alias: '/:id/:date'
}
]
使用
<router-link :to="{ name: 'school', params: { id: 10, date: '0612'} }">学校信息</router-link>
<router-link to="/20/1234">学校信息2</router-link>
导航守卫
用于在每条路由执行之前,进行的操作。实际应用在未登录用户,跳转登录等场景。
// 设置导航守卫
router.beforeEach(function (to, from, next) {
// console.log(to, from);
// next() 导航守卫结束,继续下一步导航
// next(false) 终止下一步导航
if (to.path === '/user') {
// 路由跳转
next('/category');
} else {
next();
}
})
每个导航守卫需要执行一次next()
,相当于导航守卫操作结束,否则无法进入导航路由。
History模式
Vue Router 默认使用 Hash 模式,因为兼容性更好,但是也提供了History 模式的设置。
需要通过 Vue Router 实例的 mode
选项来设置,这样 URL 会更加美观,但同样需要后端支持避免问题,防止刷新访问不到路径。
var router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Index },
{ path: '/user', component: User },
{ path: '/category', component: Category },
]
})