目录
前端路由和后端路由
- 后端路由:对于普通的网站,所有的超链接都是url地址,所有url都对应服务器上对应的资源
- 前端路由:对于单页面应用程序来说,主要通过url的hash(#)来实现不同页面的切换,同时hash还有一个特点HTTP请求中不会包含hash相关的内容,所以单页面程序中的页面跳转主要用hash实现
在单页面应用程序中这种通过hash来改变页面的方式称作前端路由区别于后端路由
Vue Router的使用
1、导入vue-router包
2、路由挂载到Vue实例中
3、每个路由规则都是一个对象,这个规则对象身上必须有两个属性
- 属性1 path表示监听哪个路由链接地址
- 属性2 component,表示如果路由是前面匹配到的path,则展示component属性对应的组件,component属性值必须是一个组件模板对象,不能是组件的引用名称
4、在控制的div中使用
5、在搜索栏输入对应的匹配规则,login
6、使用vue官方提供的router-link元素使用,它默认渲染为一个a标签
vue路由属性
1、to属性
//vue路由中to属性的作用中做跳转链接,相当于a标签的“href”属性。
<router-link :to="/home">Home</router-link>
<!--渲染结果-->
<a href="/home">Home</a>
2、replace属性
//vue路由中replace属性的作用是页面切换时不会留下历史记录。
<router-link :to="/home"replace></router-link>
3、tag属性
<router-link to="/login" tag="span">登录</router-link>
<router-link to="/register">注册</router-link>
运行程序检查元素,如下图:渲染为一个 span标签了
但是这个 登录 ,依然可以点击切换,说明不管你把它展示成为啥标签,在 router-link内部 或者说 vue-router内部,会永远为它绑定一个点击的触发事件。
4、name属性
一个对象中存在三个属性,分别是代表路径的 path 、代表组件的 component 、以及name。这个 name 属性的意义就在于,我们可以使用这个name 属性来动态的绑定 当前路由,而不是只能跳转固定的 path路径,也就是说,我一旦 给某个路由设置了 name 属性,那么我在跳转路由的时候,如果是通过绑定的 name 属性值 进行跳转,而不是根据绑定的 path 路径来跳转的话,那么,我的跳转就可以是动态的了,即使这个路由的 path 更改了,我的 name也是指向当前更改后的路由
//配置路由
const routes = [
{ path: '/', name:"indexLink", component: Home },
{ path: '/menu', name:"menuLink", component: Menu },
{ path: '/login', component: Login },
{ path: '/register', component: Register },
{ path: '/admin', component: Admin },
{ path: '/about', component: About },
{ path: '*', redirect: "/menu" },
]
当我们在定义了 name 属性之后,我们可以使用 绑定的方法 v-bind: 或者 :来在 router-link 的 to 属性中,绑定该路径
<template>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<ul class="navbar-nav">
<li>
<router-link :to="{name : 'indexLink'}" class="nav-link" >主页</router-link>
</li>
<li>
<router-link :to="{name : 'menuLink'}" class="nav-link" >菜单</router-link>
</li>
<li>
<router-link to="admin" class="nav-link">管理</router-link>
</li>
<li>
<router-link to="about" class="nav-link">关于我们</router-link>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li>
<router-link to="login" class="nav-link">登录</router-link>
</li>
<li>
<router-link to="register" class="nav-link">注册</router-link>
</li>
</ul>
</nav>
</header>
</template>
路由的跳转方式
1、第一种就是我们很熟悉的直接在 router-link 里面 to 属性后接路径,或者是通过路由 name 属性绑定
<li>
<router-link :to="/" class="nav-link" >主页</router-link>
<router-link :to="{name : 'indexLink'}" class="nav-link" >主页</router-link>
</li>
2、绑定一个方法,通过方法触发
<template>
<div>
<h1>HOME</h1>
<button @click="goToMenu" class="btn">返回上一次浏览页面</button>
</div>
</template>
<script>
export default({
methods: {
goToMenu(){
// this指代的是对象 $router是固定方法,go(-1)则是上次浏览页面
this.$router.go(-1)
}
}
})
</script>
3、指定跳转的地址:通过replace 方法.通过 replace(),指定需要跳转到的路由的 path 路径
<template>
<div>
<h1>HOME</h1>
<button @click="goToMenu" class="btn">点击跳转</button>
</div>
</template>
<script>
export default({
methods: {
goToMenu(){
this.$router.replace('/menu')
}
}
})
</script>
4、跳转到指定的路由名字下:通过 replace(),指定需要跳转的路由的 name 属性{name:'menuLink'}
template>
<div>
<h1>HOME</h1>
<button @click="goToMenu" class="btn">点击跳转</button>
</div>
</template>
<script>
export default({
methods: {
goToMenu(){
this.$router.replace({name:'menuLink'})
}
}
})
</script>
5、最常用的,push 方法:该方法和replace方法一样,即可 通过路由地址跳转,也可通过路由名字跳转
<template>
<div>
<h1>HOME</h1>
<button @click="goToMenu" class="btn">点击跳转</button>
</div>
</template>
<script>
export default({
methods: {
goToMenu(){
this.$router.push('/menu')
this.$router.push({name:'menuLink'})
}
}
})
</script>
replace 和 push 都能进行路由的 path路径、name属性 跳转,name他们的区别在哪里?
replace:通过replace 跳转的路由,不会存在在浏览器的 历史记录里,只会将需要跳转的路由替换当前的路由
push:通过push 跳转的路由,存在于浏览器的历史记录中,可以通过 浏览器的前进后退按钮。回到之前会之后的路由
路由动态匹配
const User = {
template: '<div>User</div>'
}
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
现在呢,像 /user/foo 和 /user/bar 都将映射到相同的路由。
一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板,输出当前用户的 ID:
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
你可以在一个路由中设置多段“路径参数”,对应的值都会设置到 $route.params 中。例如:
模式 | 匹配路径 | $route.params |
/user/:username | /user/evan | { username: 'evan' } |
/user/:username/post/:post_id | /user/evan/post/123 | { username: 'evan', post_id: '123' } |
响应路由参数的变化
提醒一下,当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象:
const User = {
template: '...',
watch: {
$route(to, from) {
// 对路由变化作出响应...
}
}
}
const User = {
template: '...',
beforeRouteUpdate(to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
捕获所有路由或 404 Not found 路由
常规参数只会匹配被 / 分隔的 URL 片段中的字符。如果想匹配任意路径,我们可以使用通配符 (*):
{ // 会匹配所有路径 path: '*' } { // 会匹配以 `/user-` 开头的任意路径 path: '/user-*' }
当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后。路由 { path: '*' } 通常用于客户端 404 错误。如果你使用了History 模式,请确保正确配置你的服务器。
当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分:
// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'
路由的嵌套
使用children属性实现路由嵌套,子路由path前不要加/,否则永远以根路径开始请求
路由-二三级路由跳转
1、新建项目之后,在 components 文件夹下,新建 about 文件夹
2、引入二级路由,同样的是在main.js 文件下引入,这四个组件都是在 components/about 这个文件夹下,所以路径要写对
//二级路由引入
import Contact from './components/about/Contact'
import Delivery from './components/about/Delivery'
import History from './components/about/History'
import OrderingGuide from './components/about/OrderingGuide'
3、配置二级路由,因为二级路由都是在 about.vue 页面显示的,所以应该写在一级路由 about 的内部,在内部添加一个 children 属性,值为一个数组,数组内部存在多个路由对象,对象内部存在 path 、name、component 三个属性
//配置路由
const routes = [
{ path: '/', name:"indexLink", component: Home },
{ path: '/menu', name:"menuLink", component: Menu },
{ path: '/login', name:"loginLink", component: Login },
{ path: '/register', name:"registerLink", component: Register },
{ path: '/admin', name:"adminLink", component: Admin },
{ path: '/about', name:"aboutLink", component: About ,children:[
{ path: '/about/contact', name:'contactLink' , component: Contact },
{ path: './delivery', name:'deliveryLink' , component: Delivery },
{ path: './history', name:'historyLink' , component: History },
{ path: './orderingGuide', name:'orderingGuideLink' , component: OrderingGuide },
]},
{ path: '*', redirect: "/menu" },
]
页面效果:
4、配置3级路由。若是还存在三级路由,那就找到对应页面,在对应页面路由的内部继续引入,我现在是在联系我们内部引入的,所以新建一个文件夹,用来盛放三级路由
5、 重复上述引入二级路由步骤,引入三级路由,注意路由路径。components文件夹下的一级文件,就是一级路由,about文件夹下的一级文件,就是二级路由,contact文件夹下的一级文件,就是三级路由
//三级路由引入
import Phone from './components/about/Contact/Phone'
import PersionName from './components/about/Contact/PersionName'
6、重复上述配置二级路由步骤,配置三级路由
//配置路由
const routes = [
// 一级路由
{ path: '/', name:"indexLink", component: Home },
{ path: '/menu', name:"menuLink", component: Menu },
{ path: '/login', name:"loginLink", component: Login },
{ path: '/register', name:"registerLink", component: Register },
{ path: '/admin', name:"adminLink", component: Admin },
{ path: '/about', name:"aboutLink", redirect: "/about/contact", component: About ,children:[
// 二级路由
{ path: '/about/contact', name:'contactLink' ,redirect:"/about/contact/phoneLink", component: Contact ,children:[
// 三级路由
{ path: '/about/contact/phoneLink',name:"phoneLink", component: Phone },
{ path: './persionNameLink',name:"persionNameLink", component: PersionName }
]},
{ path: './delivery', name:'deliveryLink' , component: Delivery },
{ path: './history', name:'historyLink' , component: History },
{ path: './orderingGuide', name:'orderingGuideLink' , component: OrderingGuide },
]},
{ path: '*', redirect: "/menu" },
]
7、二级路由页面。点击切换二级路由
<template>
<div class="row mb-5">
<div class="col-4">
<!-- 导航 -->
<div class="list-group mb-5">
<router-link tag="li" :to="{name: 'historyLink'}">
<a class="list-group-item list-group-item-action">历史订单</a>
</router-link>
<router-link tag="li" :to="{name: 'contactLink'}">
<a class="list-group-item list-group-item-action">联系我们</a>
</router-link>
<router-link tag="li" :to="{name: 'orderingGuideLink'}">
<a class="list-group-item list-group-item-action">点餐文档</a>
</router-link>
<router-link tag="li" :to="{name: 'deliveryLink'}">
<a class="list-group-item list-group-item-action">快递信息</a>
</router-link>
</div>
</div>
<div class="col-8">
<!-- 导航所对应的内容 点击切换对应的二级路由页面 且 展示-->
<router-view></router-view>
</div>
</div>
</template>
8、三级路由页面。点击切换三级路由
<template>
<div class="card text-dark bg-light mb-3">
<div class="card-header">联系我们</div>
<div class="card-body">
<h4 class="card-title">联系我们</h4>
<p class="cad-text">3104527996@qq.com</p>
<div>
<router-link :to="{name :'phoneLink'}">电话</router-link>
<router-link :to="{name :'persionNameLink'}">联系人</router-link>
<!-- 点击上面的 router-link 切换对应三级路由 且 展示-->
<router-view></router-view>
</div>
</div>
</div>
</template>
此时,页面已经可以正常切换二级和三级路由了,但是在二级和三级路由中,没有设置默认显示页面,所以在配置 二级 和 三级 路由时设置 redirect 属性,以此来指定显示的二级 和三级 路由, ps:但是,需要注意的是,在一级路由里指定二级路由时,需要是完全一样的路径,其实path: './delivery', 和 path: '/about/contact', 两个在页面中显示的路径都是一样的,但是指定的时候必须要写成 path: '/about/contact', 这样的
//配置路由
const routes = [
{ path: '/', name:"indexLink", component: Home },
{ path: '/menu', name:"menuLink", component: Menu },
{ path: '/login', name:"loginLink", component: Login },
{ path: '/register', name:"registerLink", component: Register },
{ path: '/admin', name:"adminLink", component: Admin },
{ path: '/about', name:"aboutLink", redirect: "/about/contact", component: About ,children:[
{ path: '/about/contact', name:'contactLink' ,redirect:"/about/contact/phoneLink", component: Contact ,children:[
{ path: '/about/contact/phoneLink',name:"phoneLink", component: Phone },
{ path: './persionNameLink',name:"persionNameLink", component: PersionName }
]
},
{ path: './delivery', name:'deliveryLink' , component: Delivery },
{ path: './history', name:'historyLink' , component: History },
{ path: './orderingGuide', name:'orderingGuideLink' , component: OrderingGuide },
]},
{ path: '*', redirect: "/menu" },
]
路径配置时,直接写 /about 类似的就是直接在根目录下的,若是写成 ./delivery 类似的,前面加 . 的,就代表是在当前路径下
路由重定向
路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面;通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:
路由传参
1.如果使用查询字符串 给路由传递参数则不需要修改路由规则的path属性
2.使用query传递参数
3.通过params方式传递路由参数,login后面会被解析为id的值
路由传参分为 params 传参与 query 传参
params 传参类似于网络请求中的 post 请求,params 传过去的参数不会显示在地址栏中(但是不能刷新)。params 只能配合 name 使用,如果提供了 path,params 会失效。
query 传参类似于网络请求中的 get 请求,query 传过去的参数会拼接在地址栏中(?name=xx)。query 较为灵活既可以配合 path 使用,也能配合 name 使用
name是什么呢?name 是配置路由时给 path 取的别名,方便使用。但要注意的是 “地址栏显示的路径始终是 path 的值”
name 最重要的一点就是配合 params 进行路由的参数传递。我们来看一个列子:当我们登录之后我们需要把用户名带到主页进行展示。当然方法有许多比如localStorage,sessionStorag,中央事件总线bus,但我们这里需要学习路由传参。
方式一:通过 params 传参
方式二:通过 query 传参
params 传参后,刷新页面会失去拿到的参数。所以路由参数要修改为 '/login/:username'(官方称为动态路由)
但是这样就不会类似于 post 请求,他会把接收到的参数替换作为地址。
假如传入参数为:params: { username: ‘admin’},那么最终访问的地址为:http://localhost:8080/home/admin
总结
- 通过登录的例子来看,如果用户名不是敏感信息,可以直接放在地址栏中(使用query参数)
- 为什么不用params传参?由于 params 传参不能刷新。或满足刷新需求,但要对地址进行修改,用户名一样会显示在地址栏中
编程式的导航
注意:在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push。
想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)。
如果提供了 path,params 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path:
动态路由传参
- 直接调用$router.push 实现携带参数的跳转
- 配置params
- query传递的参数会显示在url后面?id=?
注意: 在子组件中 获取参数的时候是$route.params 而不是$router !!!
路由-路由抽离
路由直接引入在 main.js 文件内部,而main.js 内部还需要引入其他的文件,这就不可避免的造成了main.js内部文件过大,代码过于臃肿,所以,我们需要将引入的路由抽离出来,直接在main.js内部引用这个文件,这样可以大大的缩减main.js内部的代码量
1、新建router.js文件,将路由全部引入这个文件中
//一级路由引入
import Home from './components/Home'
import Menu from './components/Menu'
import Login from './components/Login'
import Register from './components/Register'
import Admin from './components/Admin'
import About from './components/about/About'
//二级路由引入
import Contact from './components/about/Contact'
import Delivery from './components/about/Delivery'
import History from './components/about/History'
import OrderingGuide from './components/about/OrderingGuide'
//三级路由引入
import Phone from './components/about/Contact/Phone'
import PersionName from './components/about/Contact/PersionName'
export const routes = [ //配置路由
{ path: '/', name:"indexLink", component: Home },
{ path: '/menu', name:"menuLink", component: Menu },
{ path: '/login', name:"loginLink", component: Login },
{ path: '/register', name:"registerLink", component: Register },
{ path: '/admin', name:"adminLink", component: Admin },
{ path: '/about', name:"aboutLink", redirect: "/about/contact", component: About ,children:[
{ path: '/about/contact', name:'contactLink' ,redirect:"/about/contact/phoneLink", component: Contact ,children:[
{ path: '/about/contact/phoneLink',name:"phoneLink", component: Phone },
{ path: './persionNameLink',name:"persionNameLink", component: PersionName }
]},
{ path: '/delivery', name:'deliveryLink' , component: Delivery },
{ path: '/history', name:'historyLink' , component: History },
{ path: '/orderingGuide', name:'orderingGuideLink' , component: OrderingGuide },
]},
{ path: '*', redirect: "/menu" },
]
或者:
我们将路由文件单离出来放在 routes.js 之后,在mian.js 是不能直接引用的,因为我们还没有释放这个 配置的路由,所以,在配置路由的 const 声名之前,添加 export 来释放已配置的路由,使其可以全局调用
2、在main.js内部调用该路由文件,使用的也是import引入方法
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App'
import routes from './routes' //引入路由文件
Vue.config.productionTip = false
Vue.use(VueRouter) //使用路由
const router = new VueRouter({ //实例化路由对象,将routes配置的路由放进去
routes, //传递routes路由数组
mode : "history" //干掉浏览器#
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router, //将 路由 挂载到根实例对象中 需要通过router 配置参数注入路由
components: { App },
template: '<App/>'
})
3、如果是侧边菜单栏的话,可以这样组成路由文件
3.1、在src目录下,新建一个routers文件夹,该文件夹下,新建一个index.js文件,用来盛放所有路由
3.2、对于左侧的菜单,可以针对每个菜单新建一个路由文件,然后将这个菜单的二级、三级路由,全部引入到这个文件,然后export 将其暴露出来
import Container from "@/layout/container"
const servicePage = [
{
path: "/businessmanage",
component: Container,
meta: {
title: "企业管理",
icon: "el-icon-s-claim",
menuShow: true
},
children: [
{
path: "/businessmanage",
component: () => import("@/views/Businessmanage/Businessmanage"),
name: "企业信息",
meta: {
title: "企业信息"
},
show: true
},
{
path: "/businesslist",
component: () => import("@/views/Businessmanage/Businesslist"),
name: "企业账户列表",
meta: {
title: "企业账户列表"
},
show: true
},
{
path: "/balancelist",
component: () => import("@/views/Businessmanage/balancelist.vue"),
name: "账户余额列表",
meta: {
title: "账户余额列表"
},
show: true
},
{
path: "/graderank",
component: () => import("@/views/Businessmanage/graderank.vue"),
name: "企业等级配置",
meta: {
title: "企业等级配置"
},
show: true
}
]
}
]
export default servicePage
3.3、对于每个菜单,都新建一个文件,且每个文件都将其暴露出来,以供引用
3.4、在index.js文件中,引入这些文件,作为二级路由,在children中引入,且 使用数组扩展运算符,将其展开。将组合的路由数组,暴露出来
import Vue from "vue"
import Router from "vue-router"
import Layout from "@/layout/Layout"
import HomePage from "./modules/home"
import Dataversion from "./modules/dataversion"
import management from "./modules/management"
import Franchisee from "./modules/franchisee"
import order from "./modules/order"
import marketTools from "./modules/marketTools"
import subCommission from "./modules/subCommission"
import setup from "./modules/setup"
// 重写push方法 解决重复点击相同路由报错
const routerPush = Router.prototype.push
Router.prototype.push = function push(location) {
return routerPush.call(this, location)
}
Vue.use(Router)
export const constantRouterMap = [
{
path: "/",
redirect: "/home",
component: Layout,
children: [...HomePage, ...management, ...Franchisee, ...Dataversion, ...order, ...setup, ...marketTools, ...subCommission],
hidden: true
}
]
const router = new Router({
mode: "history",
routes: constantRouterMap
})
export default router
3.5、在main.js文件中,引入这个index.js文件,且挂载到根组件上
import Vue from "vue"
import App from "./App.vue"
import router from "./router/index"
import store from "./store"
import "@/assets/iconfont/iconfont.css"
import "@/style/index.less"
import "./permission"
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app")