Vue Router 是 Vue.js 的官方插件,用来快速实现单页应用。
需要应用时就引入, 不需要时就不用引用.
单页应用
SPA(Single Page Application)单页面应用程序,简称单页应用。指的是网站的 “所有” (大部分或某部分)功能都在单个页面中进行呈现。
具有代表性的有后台管理系统、移动端、小程序等。
- 如果一个页面只有一个功能 - 多页应用 (传统方式) - 主要是电商
单页应用优点:
-
前后端分离开发,提高了开发效率。
-
业务场景切换时,局部更新结构。
-
用户体验好,更加接近本地应用。
单页应用缺点:
-
不利于 SEO。
-
初次首屏加载速度较慢。
-
页面复杂度比较高。
前端路由
前端路由,指的是 URL 与内容间的映射关系
- 三要素: URL、内容、映射关系
两种实现方式:
- Hash 方式
- History 方式
Hash 方式
通过 hashchange
事件监听 hash
变化,并进行网页内容更新。
hash
是url的一部分 (#
后面的部分), hash
会修改url但是不会跳转.
<body>
<div>
<a href="#/">首页</a>
<a href="#/category">分类页</a>
<a href="#/user">用户页</a>
</div>
<div id="container">
这是首页功能
</div>
<script>
var container = document.getElementById('container');
window.onhashchange = function () {
// 获取 url 中的 hash
var hash = location.hash.replace('#', '');
// 根据不同 hash 值,更改网页内容(功能)
var str = '';
switch (hash) {
case '/':
str = '这是首页的功能';
break;
case '/category':
str = '这是分类的功能';
break;
case '/user':
str = '这是用户的功能';
break;
}
container.innerHTML = str;
};
</script>
</body>
location.hash
得到的hash值是带#
的, 所以用replace
去除hash
值.
hash封装
封装以备复用。
router
就是我们准备的路由功能的对象routes
是个对象, 用来存储多个路由, 即url(hash)和内容处理函数的对应关系route
是用来定义路由的函数,path
是路径,callback
是功能.init
方法用来进行路由的初始化操作,&&
只有前面的一个为true
时才能执行第二个
<body>
<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>
</body>
Hash 方式特点总结:
-
Hash 方式兼容性好。
-
地址中具有
#
,不太美观。 -
前进后退功能较为繁琐。
History 方式
History 方式采用 HTML5 提供的新功能实现前端路由。兼容性没有hash
好.
在操作时需要通过 history.pushState()
变更 URL并执行对应操作。
history.pushState()
的参数:
- 参数一: 与当前路径相关的状态对象
- 参数二: 用来传入标题(目前浏览器不支持)
- 参数三: 当前希望将url设置为哪个路径, 但不会出现跳转操作
- 直接用
.href
属性获取到的是整个url的值 getAttribute()
获取的是href
属性它内部的值, 也就是"/"
,"/category"
,"/user"
这些值.
前进后退处理
- 前进后退功能,首先需要在更改 url 时保存路由标记。更改
pushState()
的第一个参数. - 通过
popstate
事件监听前进后退按钮操作,并检测state
, 如果有就去到相应的路径, 没有就返回首页。 router.init()
调用初始化方法监听前进后退操作并处理。
<body>
<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>
</body>
Vue Router
是 Vue.js 官方的路由管理器,让构建单页面应用变得易如反掌。
基本使用
如何安装vue router?
- 直接下载 / CDN
- npm
npm install vue-router
如果是引入的形式, 要在vue.js后面引用.
基本结构
Vue Router 提供了用于进行路由设置的组件 <router-link>
与<router-view>
。
-
<router-link>
用于切换,使用to
设置路径; 默认是<a>
标签, 如果希望是其他形式(比如<li>
), 可以使用tag
更改. -
<router-view>
用来显示路由匹配到的组件
具体操作
- 定义路由中需要的组件,并进行路由规则设置。
- 定义路由中需要的组件,并进行路由规则设置。
- 创建 Vue Router 实例,通过 routes 属性配置路由。
- 创建 Vue 实例,通过 router 属性注入路由。
命名视图
<router-view>
只能显示一个视图, 如果导航后,希望同时在同级展示多个视图(组件),这时就需要进行命名视图 (用于区分不同的视图)。
- 有一个或多个
<router-view>
时只能有一个不命名, 默认为name="default"
路由中通过 components
属性进行设置不同视图的对应组件:
- 左边(如
default
)指的是:<router-view>
的name
- 右边(如
SideBar1
)指的是: 组件的名称
<body>
<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>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
var SideBar1 = {
template: `<div>侧边栏1功能</div>`
};
var SideBar2 = {
template: `<div>侧边栏2功能</div>`
};
var Index = {
template: `<div>首页功能</div>`
};
var User = {
template: `<div>用户功能</div>`
};
// 定义路由规则
var routes = [
{
path: '/',
components: {
// router-view 的 name : 组件配置对象
default: Index,
sidebar: SideBar1
}
},
{
path: '/user',
components: {
default: User,
sidebar: SideBar2
}
}
];
// 创建 Vue Router 实例
var router = new VueRouter({
routes
});
// 创建 Vue 实例
new Vue({
el: '#app',
router
});
</script>
动态路由
当我们需要将某一类 URL 都映射到同一个组件,就需要使用动态路由。
定义路由规则时,将路径中的某个部分使用 :
进行标记,即可设置为动态路由。
设置为动态路由后,动态部分为任意内容均跳转到同一组件。比如用户1,2,3都会跳转到同一个页面:
:
部分对应的信息称为路径参数,存储在 vm.$route.params
中, 如果设置动态路由时使用的是:id
, 那么这里就用.id
获取动态变化内容.
- 这里
$route.params.id
<body>
<div id="app">
<router-link to="/user/1">用户1</router-link>
<router-link to="/user/2">用户2</router-link>
<router-link to="/user/3">用户3</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// 设置组件
var User = {
template: `<div>这是用户 {{ $route.params.id }} 的功能</div>`
};
// 设置路由规则
var routes = [
{
path: '/user/:id', component: User
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el: '#app',
router
});
</script>
</body>
侦听路由参数
如果要响应路由的参数变化,可以通过 watch
监听 $route
。
- 动态路由切换时, 组件是复用而不是重新创建.
- 当切换不同的用户时,
$route
就会发生变化, 就会调用watch
, 这是route
的内容如下. 其中route.params.id
就是对应的动态路由的内容.
路由传参处理 (单个视图)
通过路由的 props
设置数据,并通过组件 props
接收。
发送方:
- 如果设置了
props:true
, 接收的时候就使用props
接收. 实际上是把$route.params.id
的值传到了props
里
接收方:
<body>
<div id="app">
<router-link to="/user/1">用户1</router-link>
<router-link to="/user/2">用户2</router-link>
<router-link to="/user/3">用户3</router-link>
<router-link to="/category/1">分类1</router-link>
<router-link to="/category/2">分类2</router-link>
<router-link to="/category/3">分类3</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// 组件的配置对象
var User = {
template: `<div>这是用户 {{ $route.params.id }} 功能</div>`
};
var Category = {
props: ['id'],
template: `<div>这是分类 {{ id }} 功能</div>`
};
// 设置路由规则
var routes = [
{
path: '/user/:id',
component: User
},
{
path: '/category/:id',
component: Category,
props: true
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el: '#app',
router
});
</script>
</body>
路由传参处理 (多个视图)
包含多个命名视图时,需要将路由的 props
设置为对象。
-
如果组件中不需要传值, 可以写成
default
或者不写. -
如果希望设置静态数据,可将 props 中的某个组件对应的选项设置为对象,内部属性会绑定给组件的 props。
<body>
<div id="app">
<router-link to="/user/1">用户1</router-link>
<router-link to="/user/2">用户2</router-link>
<router-link to="/user/3">用户3</router-link>
<router-link to="/category/1">分类1</router-link>
<router-link to="/category/2">分类2</router-link>
<router-link to="/category/3">分类3</router-link>
<router-view name="sidebar"></router-view>
<router-view name="sidebar2"></router-view>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
var SideBar = {
template:`<div>这是SideBar组件</div>`
};
var SideBar2 = {
props: ["a","b"],
template:`<div>sidebar2: {{a}} {{b}}</div>`
};
var User = {
template:`<div>这是用户{{$route.params.id}}的组件</div>`
};
var Category = {
props: ['id'],
template: `<div>这是分类{{id}}的组件</div>`
};
var routes = [
{
path: "/user/:id",
components: {
sidebar: SideBar,
default: User
}
},
{
path: "/category/:id",
components: {
sidebar: SideBar,
sidebar2: SideBar2,
default: Category
},
props: {
sidebar: false,
sidebar2: {
a: "状态1",
b: "状态2"
},
default: true
}
}
];
var router = new VueRouter({ routes })
var vm = new Vue({
el: "#app",
router
})
</script>
</body>
嵌套路由
实际场景中,路由通常由多层嵌套的组件组合而成,这时需要使用嵌套路由配置。
- 使用
children
来进行嵌套路由中的子路由设置。
<body>
<div id="app">
<router-link to="/user">用户功能</router-link>
<router-view></router-view>
</div>
<script src="./lib/vue.js"></script>
<script src="./lib/vue-router.js"></script>
<script>
var User = {
template: `
<div>
<h3>这是 User 组件的功能</h3>
<router-link to="/user/hobby">爱好功能</router-link>
<router-link to="/user/info">用户信息</router-link>
<router-view></router-view>
</div>
`
};
var UserHobby = {
template: `<div> UserHobby 组件</div>`
};
var UserInfo = {
template: `
<div>
UserInfo 组件
<router-link to="/user/info/school">学校信息</router-link>
<router-view></router-view>
</div>`
};
var UserInfoSchool = {
template: `<div> UserInfoSchool 组件</div>`
};
var routes = [
{
path: '/user',
component: User,
children: [
{
path: 'hobby',
component: UserHobby
},
{
path: 'info',
component: UserInfo,
children: [
{
path: 'school',
component: UserInfoSchool
},
]
}
]
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el: '#app',
router
});
</script>
</body>
编程式导航
编程式导航,指的是通过方法设置导航。
router.push()
用来导航到一个新 URL (在控制台中可以进行测试)。
<router-link>
的to
属性使用绑定方式(v-bind)时也可以使用属性对象结构。使用v-bind
绑定后, 实际上to
会执行router.push()
方法, 所以内部可以使用属性对象结构.
命名路由
设置路由时添加 name
属性。
在 push()
中通过 name
导航到对应路由,参数通过 params
设置。
注意:
params
用来设置路由的参数, 它只能与name
搭配; 如果采用path
的话, 必须采用完整的url地址.
也可以在 <router-link>
中使用:
<body>
<div id="app">
<router-link :to="{ name: 'school', params: { id: 10, a:2, b:3 } }">学校10</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
var School = {
template: `<div>School 组件的功能: {{ $route.params }}</div>`
};
var routes = [
{
path: '/user/:id/info/school',
name: 'school',
component: School
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el: '#app',
router
});
</script>
</body>
重定向
别名(美化路由)
alias
让显示在客户端浏览器上的url更简短和美观, 而实际上访问的是另一个比较复杂的url.
<body>
<div id="app">
<router-link :to="{name: 'school', params: {id:1, data:'0315'}}">学校信息</router-link>
<router-link to="/20/1234">学校信息2</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// 组件
var School = {
template: `
<div>School 组件</div>
`
};
// 路由规则
var router = new VueRouter({
routes: [
{
path: '/user/:id/info/school/:date',
name: "school",
component: School,
alias: ":id/:data"
}
]
});
var vm = new Vue({
el: '#app',
router
});
</script>
</body>
设置别名后, 点击"学校信息2"
时url会显示如下:
导航守卫
比如有一些页面需要对用户的访问权限进行限制, 需要拦截或进行跳转处理, 就需要导航守卫.
to
表示要跳转到的路由from
是从哪里来的路由next
如果from
和to
满足条件, 进行的下一步操作next
除了放行之外还可以传入一些参数, 比如当用户不能继续进行导航操作的时候, 可以传入false
; 如果未登录用户访问了一些需要登陆以后才能进行的操作, 就跳转到登录页面.
- 如果
next()
直接调用, 表示可以执行后续功能.
- 设置
next(false)
表示阻止了后续操作.
- 路由跳转操作
比如说当点击"用户"(/user)的时候跳转到"分类"(/category)
History 模式
大部分url是使用hash模式的, 因为它的兼容性更好, 主要特征就是url中带有#
号.
除此以外, url还有history模式(url中没有#
了), 需要通过 Vue Router 实例的 mode 选项来设置,这样 URL 会更加美观,但同样需要后端支持避免问题。