本项目将在GitHub上维护更新。
https://github.com/dangjingtao/FeRemarks
本讲接着之前的项目继续做。 如果vue-cli没有安装,那么就以插件形式引入:
vue add router
router.js设置大致是这样。
import Vue from 'vue'
import Router from 'vue-router'
import Shop from '@/pages/Shop'
Vue.use(Router)
export default new Router({
mode: 'history',
base:process.env.BASE_URL,
routes: [
{
path: '/',
name: 'shop',
component: Shop
}
]
})
在main.js中,
new Vue({
router,
render:h=>h(App)
}).$mount('#app');
vue-router默认是通过哈希路由的方式实现的。这是一种比较low的方式。 如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。 使用后对搜索引擎比较友好,好看。缺点是后端要nginx配置。
const router = new VueRouter({
mode: 'history',
routes: [...]
})
当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!
接下来就是创建一个导航路由:
<template>
<div id="app">
<div id="nav">
<router-link to='/'>home</router-link>
<router-link to='/about'>about</router-link>
</div>
<!-- 类似插槽 -->
<router-view/>
</div>
</template>
写一个匹配about的组件,在router中配置一下,那么功能就出来了
router中可以设置一个环境变量。默认当然是根路由/
.
如果需要做个性化配置,可在根目录下创建一个vue.config.js
:
// node es5-like 代码
module.exports={
publicPath:'/mall'
}
那么所有路由前面都会加上一个/mall
。
如果你想写接口,还可以这样恶搞:
// node es5-like 代码
console.log(1)
module.exports={
publicPath:'/mall',
// 配置webpack
configureWebpack:{
devServer:{
before(app){
// app是一个express服务器
app.get('/goods',(res,req)=>{
res.json([
{id:1},
{id:2}
])
})
}
}
}
}
妈爷,一个接口就有了。
router-link实际上是一个a标签。
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件。 对商城做进一步的功能划分,实现以下架构: __
嵌套时只需要关注这两点:
下面开始重构home组件
//...
routes: [
{
path: '/',
name: 'shop',
component: Shop,
children:[
{
path: '/list',
name: 'List',
component: List
},{
path:'about',
name:'About',
component:About
}
]
},
{
path: '/login',
name: 'Login',
component: Login
},
]
使用时:
<template>
<div>
<h1>home</h1>
<router-view></router-view>
</div>
</template>
如果你想在/home下面渲染默认的内容,还需要加一个空白的路由,指定router-view
的渲染内容。
{
path: '',
name: 'Default',
component: Default
},
如果我想把前列表页面(/list
)的ID传给下一页(detail)。应该怎么做?
点击时必须是router-link需要把列表的标题加上一个link
路由传参实际有四种方式。
在router中需要设置:
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
传参和调用方式如下:
// 传参
<td><router-link :to="`/detail/${item.id}`" >{{item.name}}</router-link></td>
// 调用
this.$route.params.id
不同路由的表现如下:
props参是一种与router解耦的传参方式。
{
path: '/detail/:id',
name: 'Detail',
component: Detail,
props:true
}
// 传参
this.$router.push({
name: 'Describe',
params: {
id: id
}
})
// 调用
this.$route.params.id
刷新后丢失。
param传参的弱点很明显,你刷新后,数据就丢失了。 这也是基于地址栏的路由传参。具有持久化的特点。
//传参:
this.$router.push({
path:'/xxx',
query:{
id:id
}
})
//接收参数:
this.$route.query.id
重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
重定向的目标也可以是一个命名的路由:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
甚至是一个方法,动态返回重定向目标:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
路由跳转前做一些验证,比如登录验证,是网站中的普遍需求。 比如说,我需要访问about页面。
你可以使用 router.beforeEach
注册一个全局前置守卫:
可在此判断是否登陆
// router.js
const router = new VueRouter({ ... })
// 每次进入之前都会开启监听
router.beforeEach((to,from,next)=>{
//判断是否登录
if(to.path=='/about'&&!window.isLogin){
next('/login?redirect='+to.path);
}else{
next();
}
})
登录成功后,回到原来的地址:
login() {
window.isLogin=true;
const redirect=this.$route.query.redirect;
this.$router.push({path:redirect})
}
islogin 可以放到vuex中
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
每个守卫方法接收三个参数: to: Route: 即将要进入的目标 路由对象 from: Route: 当前导航正要离开的路由 next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
确保要调用 next 方法,否则钩子就不会被 resolved。
你可以在路由配置上直接定义 beforeEnter 守卫:
const router = new VueRouter({
routes: [
{
path:'about',
name:'About',
component:About,
beforeEnter:(to,from,next)=>{
//判断是否登录
if(to.path=='/about'&&!window.isLogin){
next('/login?redirect='+to.path);
}else{
next();
}
}
}
]
})
这些守卫与全局前置守卫的方法参数是一样的。
最后,你可以在路由组件内直接定义以下路由导航守卫:
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`
}
}
beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。
beforeRouteUpdate (to, from, next) {
// just use `this`
this.name = to.params.name
next()
}
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
beforeRouteLeave (to, from , next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
导航被触发。=>在失活的组件里调用离开守卫。=>调用全局的 beforeEach 守卫。=>在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。=>在路由配置里调用 beforeEnter。=>解析异步路由组件。=>在被激活的组件里调用 beforeRouteEnter。=>调用全局的 beforeResolve 守卫 (2.5+)。=>导航被确认。=>调用全局的 afterEach 钩子。=>触发 DOM 更新。=>用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。