前端路由与后端路由作用是类似的,访问一定规则的url转向至指定的页面,只是后端路由是访问地址后由后端拦截处理后再转向,前端路由是通过不同地址局部切换不同的页面内容。当前后端彻底分离,不使用高级语言编写前端且前端是SPA应用时,就需要前端路由了。至于什么项目适合做成SPA则需要根据实际场景判断了。
vue路由引入
默认webpack模板已经安装了vue-router包,不使用脚手架可引入vue-router.js或npm安装。
webpack模板下,为了方便管理路由的配置,将路由抽离成单独文件存放在src/router/index.js中,打开文件,会看到vue-router的引用与配置:
import Router from 'vue-router' //引如vue-router包
Vue.use(Router) //使用路由
在SPA的入口文件src/main.js中引入上面的router模块:
import Vue from 'vue'
import App from './App'
import router from './router' // 引入router/index.js
new Vue({
el: '#app',
router, // 注册路由
template: '<App/>',
components: { App }
})
Router配置:
import Vue from 'vue'
import Router from 'vue-router'
import Main from '@/views/main.vue'
Vue.use(Router)
export default new Router({
mode: 'history', // 模式,影响地址栏效果。history/hash,hash模式带有#,还有一个Abstract模式当发现不支持浏览器API比如node环境强制进入该模式。
base: __dirname, // 根路径
routes:
[
{
path: '/', // 访问路径,地址栏访问http://localhost:8080/
name: 'Main', // 路由名称
component: Main //渲染视图
}
]
})
路由视图渲染
vue中使用router-view和router-link来渲染路由。
router-view是路由视图容器,实际上就是访问路由后路由配置中的视图就被填充在此标签的位置上。
router-link路由跳转,使用此标签指定跳转到某个路由视图。
上面的路由配置中访问工程根,会使用Main视图渲染路由,Main视图实际上被渲染到App.vue的<router-view/>
位置,这是因为main.js中使用的视图是app.vue。
在App.vue中创建两个链接跳转到另外两个视图
<template>
<div>
<router-link to="/news">跳转到新闻页</router-link>
<router-link to="/video">跳转到视频页</router-link>
<router-view/>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
<style>
</style>
点击链接后会发现页面内容变成了to属性与router配置匹配的path视图,但同时Main视图的内容消失了,这是因为在App.vue中做的跳转将由App.vue中的<router-view/>
来承载。
router-link属性
router-link属性中to代表要跳转的路由,tag代表用什么标签来渲染链接,默认是a,比如指定为div则会生成一个div标签,点击后跳转到指定路由。
<router-link to="/news" tag="div">跳转到新闻页</router-link>
to属性的使用要注意的是,如果直接指定path(即router配置中的path属性),则像上面这样写,但如果使用name跳转,则应该写为:to="{name:xxx}"
,即使用v-bind传一个对象,使用name跳转下面的命名路由中再说。
动态路由
在组件之间导航的过程中涉及到传参的场景时即会用到动态路由机制。
在新闻列表页中需要跳转到新闻详情页,新闻详情页需要知道新闻的id才能知道显示哪一条新闻的内容,这时候列表页向详情页跳转时需要传递一个新闻id参数。
路由配置中
export default new Router({
mode: 'history',
base: __dirname,
routes:
[
{
path: '/',
name: 'Main',
component: Main
},
{
path: '/news',
name: 'News',
component: News
},
{
path: '/newsDetail/:newsId',
name: 'NewsDetail',
component: NewsDetail
},
{
path: '/video',
name: 'Video',
component: Video
}
]
})
在path的设置上使用了:newsId即表示这个位置是一个动态参数,参数名为newsId,此时/NewsDetail/news1格式的地址将被此path匹配,当然也可以将规则设置为类似/newsDetail/:newsId/more/:moreId格式。
新闻列表页中
<template>
<div id="news">
新闻页(News.vue中的内容)
<router-link to="/NewsDetail/news1" tag="div">第一条新闻</router-link>
<router-link to="/NewsDetail/news2" tag="div">第二条新闻</router-link>
<router-link to="/NewsDetail/news3" tag="div">第三条新闻</router-link>
<router-link to="/NewsDetail/news4" tag="div">第四条新闻</router-link>
<router-link to="/NewsDetail/news5" tag="div">第五条新闻</router-link>
</div>
</template>
获取参数
在新闻详情页(NewsDetail.vue)中获取参数使用this.$route.params.参数名。
<template>
<div id="NewsDetail">
新闻详情页(NewsDetail.vue中的内容)
</div>
</template>
<script type="text/ecmascript-6">
export default {
created() {
const newsId = this.$route.params.newsId;
console.info("newsId:"+newsId);
}
}
</script>
需要说明的是
1、上面演示rouer-link中直接使用的path匹配,还可以使用name匹配,在下面的命名路由中说明。
2、当我们跳转组件时需要传递动态参数时,即/NewsDetail/news5?query1=1&query2=2
时,使用this.$route.query.参数名
来获取,params和query不同,params指的是路由规则的参数,query指的是url参数。
3、vue实例上路由参数为$route
,不是$router
。
嵌套路由
比如新闻列表页和视频列表页都需要有底部导航,但详情页都不需要,那么就需要做一个底部导航母版,母版上有底部导航和一个<router-view></router-view>
,这个母版又被渲染到主页面的<router-view></router-view>
上,这样把需要底部导航的页面做成母版的子路由,不需要底部导航的页面与母版平级,即可达到效果,主页面的<router-view></router-view>
下嵌套了母版的<router-view></router-view>
,以达到目的。
路由配置中
export default new Router({
mode: 'history',
base: __dirname,
routes:
[
{
path: '/',
name: 'Main',
component: Main,
children: [
{
path: 'news',
name: 'News',
component: News
},
{
path: 'video',
name: 'Video',
component: Video
}
]
},
{
path: '/newsDetail/:newsId',
name: 'NewsDetail',
component: NewsDetail
}
]
})
Main.vue中
<template>
<div id="main">
默认页主内容(Main.vue中的内容)
<router-view></router-view>
<div>页面底部导航</div>
</div>
</template>
App.vue中不变
<template>
<div>
<router-link to="/news" tag="div">跳转到新闻页</router-link>
<router-link to="/video">跳转到视频页</router-link>
<router-view/>
</div>
</template>
这样的话,实际上news和video路由因为是main的子路由,所以会显示在main的<router-view></router-view>
中,也就是说,当生命了子路由的时候,本路由上要有一个<router-view></router-view>
来承载子路由视图。
再比如视频详情页需要有一个视频介绍页和一个演员排行页,可以在视频详情页的路由上声明一个子路由,子路由含有视频介绍路由和演员排行路由,这时视频详情页就需要在合适位置声明一个<router-view></router-view>
来承载两个子路由的视图。
需要注意的是,在配置子路由时,path上不需要再写父路由的地址,比如父理由path为/video,子路由中的演员排行path写为top,此时router-link的to应该指向的是/video/top。
编程式路由
除了可以在视图中使用<router-link>
来跳转以外,还可以使用编程的模式来实现跳转。
视频列表页要跳转到详情:
<template>
<div id="video">
视频页(Video.vue中的内容)
<div @click="toVideoDetail('video1')">视频详情页一</div>
<div @click="toVideoDetail('video2')">视频详情页二</div>
<div @click="toVideoDetail('video3')">视频详情页三</div>
<div @click="toVideoDetail('video4')">视频详情页四</div>
<div @click="toVideoDetail('video5')">视频详情页五</div>
</div>
</template>
<script type="text/ecmascript-6">
export default {
methods: {
toVideoDetail (videoId) {
// this.$router.push('/videoDetail/' + videoId)
this.$router.push({path: '/videoDetail/' + videoId})
}
}
}
</script>
即使用$router
中的push
方法来实现跳转,上面两种写法都可以,push的时候直接给地址,或者传递一个对象。
需要注意,此时的API为$router
,不是取参数时的$route
。控制路由的是$router
,和router-link、router-view上的router是一个意思,而控制传参等动作的是$route
,不是一个东西,坑一个。
实际上router-link也是使用了$router来控制导航的,router-link默认跳转时即调用push方法。router-link三种导航方式可用:
router-link标签属性 | $router编程方法 | 说明 |
---|---|---|
不写 | push() | 跳转并在history中留下痕迹 |
replace | replace() | 跳转不在history中留下痕迹 |
append | append() | 跳转附加在当前地址上 |
push和replace和window.location.href与window.location.replace的区别是一样的。
append,比如当前页面是a,跳转到b,使用append的话就会变成是/a/b,而不是/b。
附一个router-link方式上使用replace跳转例子:
<router-link to="/NewsDetail/news1" tag="div" replace>第一条新闻</router-link>
$router
编程中也有go API来控制前进、后退,实际就是对浏览器history API的封装。
命名路由
之前在router-link的to种都是使用path来匹配,还可以匹配name值。
改一下新闻详情的跳转
<template>
<div id="news">
新闻页(News.vue中的内容)
<router-link to="/NewsDetail/news1" tag="div" replace>第一条新闻</router-link>
<router-link :to="{name:'NewsDetail',params:{newsId:'news2'}}" tag="div">第二条新闻</router-link>
<router-link to="/NewsDetail/news3" tag="div">第三条新闻</router-link>
<router-link to="/NewsDetail/news4" tag="div">第四条新闻</router-link>
<router-link to="/NewsDetail/news5" tag="div">第五条新闻</router-link>
</div>
</template>
路由配置不变:
export default new Router({
mode: 'history',
base: __dirname,
routes:
[
{
path: '/',
name: 'Main',
component: Main,
children: [
{
path: 'news',
name: 'News',
component: News
},
{
path: 'video',
name: 'Video',
component: Video
}
]
},
{
path: '/newsDetail/:newsId',
name: 'NewsDetail',
component: NewsDetail
},
{
path: '/videoDetail/:videoId',
name: 'VideoDetail',
component: VideoDetail
}
]
})
第二条新闻的跳转改成了name匹配,to中要传递一个对象,对象中指定要跳转的路由name,此时的路由传值是给对象一个params属性,params还是一个对象,对象中指定参数名。这样一来就不需要在代码中写很长的path格式了。
需要主要的是,使用命名路由时,router-link中的to,要用v-bind形式,即使用:to,又一个坑。
命名视图
正常的router-view是没有名字的,router-view是可以命名的,用以处理一个页面中有多个路由容器的场景,比如后台常见的上左右布局,可以用三个router-view,此时需要命名,但实际上这种方式完全可以不用router-view承载,所以命名视图也基本用不到,知道有这么回事吧。
代码是很简单的,<router-view name='xxx'></router-view>
上指定name,在路由的配置上原来是用component,现在用components,components是个对象,里面有一个default指向默认不带name的router-view,其他与name匹配,比如:
export default new Router({
mode: 'history',
base: __dirname,
routes:
[
{
path: '/',
name: 'Main',
components: {
default: 组件1,
viewName1: 组件2,
viewName2: 组件3
}
...
这样的话,进入/时就会同时加载三个组件放到三个router-view的位置,实际上完全可以在/本来指向的组件中实现三块区域来切分视图,可能遇到特殊场景能用的到吧。
路由样式
vue提供了一系列的css样式,来控制路由点击、过渡等效果。
激活样式
当某一个路由处于激活状态时,会默认在router-link上增加.linkActiveClass类,添加此类的样式即可修改激活状态的样式。这个类的名字也可以在实例化router时通过linkActiveClass配置项自定义。
自定义激活状态名:
export default new Router({
mode: 'history',
base: __dirname,
linkActiveClass:'item-active',
最后
在之前的项目中,虽然使用了angular但又不能做成SPA,必须是多页面应用,对于路由的处理,只能借助JAVA中url-rewrite来做,格式约定为域名/appid/componentid/infoid形式,如果改为SPA,效果可以是一样的,只是地址的处理从后端转到了前端。
SPA的最大优点是入口加载后即可加载完毕资源,SPA中没有页的概念,路由的切换实际都是局部的渲染,效率自然高,最大的缺点是不利于SEO。
SPA对SEO不友好的问题,在知乎上也看到尤雨溪本人的评论:
我就说一点,用 Vue 不代表你一定要做成 SPA。现在有些人提起 Vue 就好像一定要 CLI 全家桶,其实 Vue 从一开始就一直很注重对后端渲染的应用做渐进增强的用例,现在欧美也有很多的开发者拿 Vue 直接替代 jQuery 做常见的交互增强。对于真正适合做成 SPA 的应用,SEO 反而通常不是问题。你针对 marketing 的页面应该是静态分开部署的,app 本身则要登陆才能用,SEO 没有什么意义。少数既需要 SPA 强交互性,又对 SEO 和首屏速度有刚性需求的场景,这时候同构 SSR 就派上用场了。
作者:尤雨溪
链接:https://www.zhihu.com/question/51949678/answer/146656850
来源:知乎
vue-router中文文档:
https://router.vuejs.org/zh-cn/