文章目录
Vue核心插件之路由模块
路由的跳转原理(哈希模式)
单页应用的路由模式有两种
- 哈希模式(利用hashchange 事件监听 url的hash 的改变)
- history模式(使用此模式需要后台配合把接口都打到我们打包后的index.html上)
哈希模式原理,哈希就是锚点
window.addEventListener('hashchange', function(e) {
console.log(e)
})
核心是锚点值的改变,我们监听到锚点值改变了就去局部改变页面数据,不做跳转。跟传统开发模式url改变后立刻发起请求,响应整个页面,渲染整个页面比路由的跳转用户体验更好
示例代码如下,哈希(锚点)就是链接后面的#/XXX
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<a href="#/login">登录</a>
|
<a href="#/register">注册</a>
<div id="app"></div>
<script type="text/javascript">
var appdiv = document.getElementById('app')
// 监听哈希改变事件
window.addEventListener('hashchange', function(e) {
console.log(location.hash)
switch(location.hash) {
case "#/login":
appdiv.innerHTML = "我是登录页面"
break
case "#/register":
appdiv.innerHTML = "我是注册页面"
break
}
})
</script>
</body>
</html>
点击后的页面内容分别如下
安装和使用路由
路由是以插件的形式引入到我们的vue项目中来的
vue-router是vue的核心插件
- 1:下载 npm i vue-router -S
- 2:安装插件Vue.use(VueRouter);
- 3:创建路由对象 var router = new VueRouter();
- 4:配置路由规则 router.addRoutes([路由对象]);
路由对象{path:‘锚点值’,component:要(填坑)显示的组件} - 5:将配置好的路由对象交给Vue
- 在options中传递-> key叫做 router
- 6:留坑(使用组件)
从vue-router模块下取出来vue-router.js,在html中直接引入
测试代码如下
<!DOCTYPE html>
<html>
<head>
<title>路由的安装和使用</title>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript" src="vue.js"></script>
<!-- 引入路由插件 -->
<script type="text/javascript" src="vue-router.js"></script>
<script type="text/javascript">
var login = {
template:`
<div>我是登录页面</div>
`
}
// 安装路由插件
Vue.use(VueRouter);
// 创建路由对象
var router = new VueRouter ({
// 配置路由对象
routes:[
{path: '/login', name:'login', component:login}
]
})
new Vue({
el:'#app',
router,
template:`
<div>
<p>请在链接上加上login测试路由功能</p>
<router-view></router-view>
</div>
`,
})
</script>
</body>
</html>
这是为匹配路由的
匹配路由后,router-view会直接被组件取代
路由的跳转
路由的跳转方式有主要有以下两种形式
- 通过标签:
<router-link to='/login'></router-link>
- 通过js控制跳转
this.$router.push({path:'/login'})
通过标签跳转示例代码
<!DOCTYPE html>
<html>
<head>
<title>路由的安装和使用</title>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript" src="vue.js"></script>
<!-- 引入路由插件 -->
<script type="text/javascript" src="vue-router.js"></script>
<script type="text/javascript">
var login = {
template:`
<div>我是登录页面</div>
`
}
var register = {
template:`
<div>我是注册页面</div>
`
}
// 安装路由插件
Vue.use(VueRouter);
// 创建路由对象
var router = new VueRouter ({
// 配置路由对象
routes:[
{path: '/login', name:'login', component:login},
{path: '/register', name:'register', component:register}
]
})
new Vue({
el:'#app',
router,
template:`
<div>
<router-link to="/login">去登陆</router-link>
|
<router-link to="/register">去注册</router-link>
<router-view></router-view>
</div>
`,
})
</script>
</body>
</html>
通过js跳转示例代码
<!DOCTYPE html>
<html>
<head>
<title>路由的安装和使用</title>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript" src="vue.js"></script>
<!-- 引入路由插件 -->
<script type="text/javascript" src="vue-router.js"></script>
<script type="text/javascript">
var login = {
template:`
<div>我是登录页面</div>
`
}
var register = {
template:`
<div>我是注册页面</div>
`
}
var buy = {
template:`
<div>我是购物页面</div>
`
}
// 安装路由插件
Vue.use(VueRouter);
// 创建路由对象
var router = new VueRouter ({
// 配置路由对象
routes:[
{path: '/login', name:'login', component:login},
{path: '/register', name:'register', component:register},
{path: '/buy', name:'buy', component:buy}
]
})
new Vue({
el:'#app',
router,
template:`
<div>
<router-link to="/login">去登陆</router-link>
|
<router-link to="/register">去注册</router-link><br>
<button @click="gotoBuyPage">我要去买东西</button>
<button @click="back">返回上一页</button>
<router-view></router-view>
</div>
`,
methods:{
gotoBuyPage() {
// push跟replace是达到同样效果,但是replace是不会向history插入记录
// this.$router.push({path:'/buy'})
this.$router.replace({path:'/buy'})
},
back() {
this.$router.go(-1)
}
}
})
</script>
</body>
</html>
区别:
- this.$router.push() 跳转到指定的url,会向history插入新记录
- this.$router.replace()同样是跳转到指定的url,但是这个方法不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的
- this.$router.go(-1) 常用来做返回,读history里面的记录后退一个
vue-router中的对象(很容易混淆):
- $route 路由信息对象,只读对象
- $router 路由操作对象,只写对象
路由的传参和取参
查询参
- 配置(传参)
:to="{name:'login',query:{id:loginid}}"
- 获取(取参)
this.$route.query.id
路由参数
- 配置(传参)
:to="{name:'register',params:{id:registerid} }"
- 配置路由的规则
{ name:'detail',path:'/detail/:id'}
获取
this.$route.params.id
总结:
- :to传参的属性里 params是和name配对的 query和name或path都可以
- 使用路由参数必须要配置路由规则里面配置好参数名,否则刷新页面参数会丢失
示例代码如下,:key="$route.fullPath"比较关键,能解决只有查询参数表,路径不变而造成页面不重新渲染的问题
<!DOCTYPE html>
<html>
<head>
<title>路由的跳转</title>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript" src="vue.js"></script>
<!-- 引入路由插件 -->
<script type="text/javascript" src="vue-router.js"></script>
<script type="text/javascript">
var login={
template:`
<div>我是登录页面
<span>这是我获取到的参数: {{ msg }}</span>
</div>
`,
data(){
return {
msg:''
}
},
created(){
this.msg=this.$route.query.id
}
}
var restiger={
template:`
<div>我是注册页面
<span>这是我获取到的路由参数:{{ foo }}</span>
</div>
`,
data() {
return {
foo:''
}
},
created(){
this.foo=this.$route.params.foo
}
}
//安装路由插件
Vue.use(VueRouter);
//创建路由对象
var router= new VueRouter({
//配置路由对象
routes:[
{path:'/login',name:'login',component:login},
{path:'/restiger/:foo',name:'restiger', component:restiger},
]
})
new Vue({
el:'#app',
router,
template:`
<div>
<router-link :to="{name:'login',query:{id:'123'}}">去登录</router-link>
|
<router-link :to="{name:'restiger',params:{foo:'bar'}}">去注册</router-link>
<button @click='jslink'>js跳转去登录</button>
<router-view :key="$route.fullPath"></router-view>
</div>
`,
data(){
return {}
},
methods:{
// js跳转传参是一样的
jslink(){
this.$router.push({name:'login',query:{id:'456'}})
}
}
})
</script>
</body>
</html>
restiger中的参数,如果定义成props就更简单了,连data都不用了
<!DOCTYPE html>
...
var restiger={
template:`
<div>我是注册页面
<span>这是我获取到的路由参数:{{ foo }}</span>
</div>
`,
props:['foo']
}
...
...
})
</script>
</body>
</html>
嵌套路由
补充上一节知识点:js跳转路由传参和标签传参,路由相同而参数不同时页面不做刷新的问题—解决方案:
<router-view :key="$route.fullPath"></router-view>
代码思想
- 1:router-view的细分
- router-view第一层中,包含一个router-view
- 2:每一个坑挖好了,要对应单独的组件
- 路由配置
routes: [
{
path:'/nav',
name:'nav',
component:Nav,
//路由嵌套增加此属性
children:[
//在这里配置嵌套的子路由
]
}
]
案例
- 进入首页下面会有导航,个人中心、首页、资讯、我的之类的
案例代码如下,需要留意的是nav的命名问题
<!DOCTYPE html>
<html>
<head>
<title>路由的跳转</title>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript" src="vue.js"></script>
<!-- 引入路由插件 -->
<script type="text/javascript" src="vue-router.js"></script>
<script type="text/javascript">
var Nav={
template:`
<div>
<router-link :to="{name:'nav.index'}">首页</router-link>
|
<router-link :to="{name:'nav.pensonal'}">个人中心</router-link>
|
<router-link :to="{name:'nav.message'}">资讯</router-link>
|
<router-link :to="{name:'nav.mine'}">我的</router-link>
<router-view></router-view>
</div>
`,
}
var index={
template:`
<div>首页</div>
`,
}
var pensonal={
template:`
<div>个人中心</div>
`,
}
var message={
template:`
<div>资讯</div>
`,
}
var mine={
template:`
<div>我的</div>
`,
}
//安装路由插件
Vue.use(VueRouter);
//创建路由对象
var router= new VueRouter({
//配置路由对象
routes:[
{
path:'',
redirect:'/nav'
},
{
path:'/nav',
name:'nav',
component:Nav,
//嵌套路由增加这个属性
children:[
//配置我们的嵌套路由
{path:'',redirect:'/nav/index'},
{path:'index',name:'nav.index',component:index},
{path:'pensonal',name:'nav.pensonal',component:pensonal},
{path:'message',name:'nav.message',component:message},
{path:'mine',name:'nav.mine',component:mine},
]
}
]
})
new Vue({
el:'#app',
router,
template:`
<div>
<router-view></router-view>
</div>
`,
})
</script>
</body>
</html>
路由守卫
const router = new VueRouter({ ... }
// 前置的钩子函数 最后要执行next()才会跳转
router.beforeEach((to, from, next) => {
// ...
})
// 后置的钩子函数 已经跳转了不需要next
router.afterEach((to, from) => {
// ...
})
路由守卫主要用于检验是否登录了,没登录就跳转到登录页面不让他在其他页面停留,但是现在这种处理主要的都用请求的全局拦截来做了。大致了解一下路由守卫即可
示例代码如下
<!DOCTYPE html>
<html>
<head>
<title>路由守卫</title>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript" src="vue.js"></script>
<!-- 引入路由插件 -->
<script type="text/javascript" src="vue-router.js"></script>
<script type="text/javascript">
var Nav={
template:`
<div>
<router-view></router-view>
<router-link :to="{name:'nav.index'}">首页</router-link>
|
<router-link :to="{name:'nav.pensonal'}">个人中心</router-link>
|
<router-link :to="{name:'nav.message'}">资讯</router-link>
|
<router-link :to="{name:'nav.mine'}">我的</router-link>
</div>
`,
}
var Index={
template:`
<div>首页</div>
`,
}
var Pensonal={
template:`
<div>个人中心</div>
`,
}
var Message={
template:`
<div>资讯</div>
`,
}
var Mine={
template:`
<div>我的</div>
`,
}
//安装路由插件
Vue.use(VueRouter);
//创建路由对象
var router= new VueRouter({
//配置路由对象
routes:[
{
path:'',
redirect:'/nav'
},
{
path:'/nav',
name:'nav',
component:Nav,
//嵌套路由增加这个属性
children:[
//配置我们的嵌套路由
{path:'',redirect:'/nav/index'},
{path:'index',name:'nav.index',component:Index},
{path:'pensonal',name:'nav.pensonal',component:Pensonal},
{path:'message',name:'nav.message',component:Message},
{path:'mine',name:'nav.mine',component:Mine},
]
}
]
})
new Vue({
el:'#app',
router,
template:`
<div>
<router-view></router-view>
</div>
`,
data(){
return {
loginstate:true
}
},
methods:{
},
mounted(){
//利用路由守卫做当跳转首页时可直接跳转,跳转其他页面要等待两秒才可以跳转
router.beforeEach((to,from,next)=>{
console.log(to)
if(to.path=='/nav/index'){
next()
}else{
setTimeout(function(){
next()
},2000)
}
})
}
})
</script>
</body>
</html>
购物车实战
下面的一个小例子是综合上面的所有知识点做的一个小demo
搭建一个显示内容的框架
在data数组中存放4个商品信息
{text:'springcloud',price:20},
{text:'vue',price:30},
{text:'js',price:40},
{text:'php',price:50},
在div中通过v-for显示出来
<ul>
<li v-for='(list,index) in classlist'>
课程名称:{{list.text}}---价格:{{list.price}}
<button @click='addtochat(index)'>添加到购物车</button>
</li>
</ul>
加入一个按钮,输入商品信息后点击,把输入的内容追加到classlist中,并清空输入的内容,整个部分代码如下
<!DOCTYPE html>
<html>
<head>
<title>购物车</title>
<style type="text/css">
span{cursor: pointer;}
</style>
</head>
<body>
<div id="app">
<div>
课程:<input type="text" name="" v-model='course'>
价钱:<input type="text" name="" v-model='price'>
<button @click='addcourse'>添加商品</button>
</div>
<ul>
<li v-for='(list,index) in classlist'>
课程名称:{{list.text}}---价格:{{list.price}}
<button @click=''>添加到购物车</button>
</li>
</ul>
</div>
<script type="text/javascript" src="vue.js"></script>
<script type="text/javascript">
new Vue({
el:'#app',
data(){
return {
classlist:[
{text:'springcloud',price:20},
{text:'vue',price:30},
{text:'js',price:40},
{text:'php',price:50},
],
course:'',
price:'',
}
},
methods:{
//添加课程
addcourse(){
//插入数据到我们的商品库
this.classlist.push({text:this.course,price:this.price})
//清空我们刚输入的商品信息
this.course=''
this.price=''
},
},
})
</script>
</body>
</html>
完成购物车大体功能
在本小结中,加入购物车的table,在点击商品添加到购物车后,能在购物车显示出来,在购物车中还能对商品进行±操作,自动根据商品个数计算出商品价格
<!DOCTYPE html>
<html>
<head>
<title>购物车</title>
<style type="text/css">
span{cursor: pointer;}
</style>
</head>
<body>
<div id="app">
<div>
课程:<input type="text" name="" v-model='course'>
价钱:<input type="text" name="" v-model='price'>
<button @click='addcourse'>添加商品</button>
</div>
<ul>
<li v-for='(list,index) in classlist'>
课程名称:{{list.text}}---价格:{{list.price}}
<button @click='addtochat(index)'>添加到购物车</button>
</li>
</ul>
<div>
购物车
<table border="1">
<tr>
<th>选中</th>
<th>课程</th>
<th>数量</th>
<th>价格</th>
</tr>
<tr v-for="(chat,index) in chatarr">
<td><input type="checkbox" name="" v-model='chat.active'></td>
<td>{{chat.text}}</td>
<td>
<span @click='reducecount(index)'>-</span>
{{chat.count}}
<span @click='addcount(index)'>+</span>
</td>
<td>{{chat.count * chat.price}}</td>
</tr>
</table>
</div>
</div>
<script type="text/javascript" src="vue.js"></script>
<script type="text/javascript">
new Vue({
el:'#app',
data(){
return {
classlist:[
{text:'springcloud',price:20},
{text:'vue',price:30},
{text:'js',price:40},
{text:'php',price:50},
],
course:'',
price:'',
chatarr:[], // 购物车数组
}
},
methods:{
//添加课程
addcourse(){
//插入数据到我们的商品库
this.classlist.push({text:this.course,price:this.price})
//清空我们刚输入的商品信息
this.course=''
this.price=''
},
// 添加到购物车
addtochat(index){
const goods=this.classlist[index]
const result=this.chatarr.find(v=>v.text==goods.text)
if(result){
result.count+=1
}else{
this.chatarr.push({...goods,count:1,active:true}) // es6展开数组
}
},
// 增加购物车商品数
addcount(index){
this.chatarr[index].count++
},
//减少购物车商品数量
reducecount(index){
if(this.chatarr[index].count>1){
this.chatarr[index].count--
}else{
if(window.confirm(`是否删除${this.chatarr[index].text}?`)){
this.chatarr.splice(index,1)
}
}
}
},
})
</script>
</body>
</html>
完成本地存储,重构做成组件的形式
本地存储采用,加入计算属性自动结算购物车,并把购物车的代码做成重构组件,方便后期进行维护;最后采用watch来实现将购物车信息保存到浏览器存储中,防止刷新页面丢失信息
<!DOCTYPE html>
<html>
<head>
<title>购物车</title>
<style type="text/css">
span{cursor: pointer;}
</style>
</head>
<body>
<div id="app">
<div>
课程:<input type="text" name="" v-model='course'>
价钱:<input type="text" name="" v-model='price'>
<button @click='addcourse'>添加商品</button>
</div>
<ul>
<li v-for='(list,index) in classlist'>
课程名称:{{list.text}}---价格:{{list.price}}
<button @click='addtochat(index)'>添加到购物车</button>
</li>
</ul>
<Chat :chatarr='chatarr'></Chat>
</div>
<script type="text/javascript" src="vue.js"></script>
<script type="text/javascript">
var Chat = {
props:['chatarr'],
template:`
<div>
购物车
<table border="1">
<tr>
<th>选中</th>
<th>课程</th>
<th>数量</th>
<th>价格</th>
</tr>
<tr v-for="(chat,index) in chatarr">
<td><input type="checkbox" name="" v-model='chat.active'></td>
<td>{{chat.text}}</td>
<td>
<span @click='reducecount(index)'>-</span>
{{chat.count}}
<span @click='addcount(index)'>+</span>
</td>
<td>{{chat.count * chat.price}}</td>
</tr>
<tr>
<td colspan='2'>选中的课程:{{activeCount}}/{{count}}</td>
<td colspan='2'>需付金额:{{totalpirce}}</td>
</tr>
</table>
</div>
`,
// 计算属性,实时计算需付金额
computed:{
//已选中课程
activeCount(){
return this.chatarr.filter(v=>v.active).length
},
//购物车存在多少项课程
count(){
return this.chatarr.length
},
//总价
totalpirce(){
let total=0
this.chatarr.forEach(v=>{
if(v.active){
total+=v.price*v.count
}
})
return total
}
},
// 用watch来实现将购物车信息保存到浏览器存储中,防止刷新页面丢失信息
watch: {
// 监听购物车数组,每当监听到改变就存进本地存储做数据持久化
chatarr:{
handler(){
window.localStorage.setItem('chat',JSON.stringify(this.chatarr)) // json序列号保存到浏览器缓存中
},
deep:true // chatarr是对象,所以需要深度监听
}
},
methods:{
// 增加购物车商品数
addcount(index){
this.chatarr[index].count++
},
//减少购物车商品数量
reducecount(index){
if(this.chatarr[index].count>1){
this.chatarr[index].count--
}else{
if(window.confirm(`是否删除${this.chatarr[index].text}?`)){
this.chatarr.splice(index,1)
}
}
}
}
}
new Vue({
el:'#app',
components:{
Chat
},
data(){
return {
classlist:[
{text:'springcloud',price:20},
{text:'vue',price:30},
{text:'js',price:40},
{text:'php',price:50},
],
course:'',
price:'',
chatarr:[], // 购物车数组
}
},
methods:{
//添加课程
addcourse(){
//插入数据到我们的商品库
this.classlist.push({text:this.course,price:this.price})
//清空我们刚输入的商品信息
this.course=''
this.price=''
},
// 添加到购物车
addtochat(index){
const goods=this.classlist[index]
const result=this.chatarr.find(v=>v.text==goods.text) // v=>v.text为es6的属性
if(result){
result.count+=1
}else{
this.chatarr.push({...goods,count:1,active:true}) // es6展开数组
}
},
},
created(){
//利用本地存储做数据持久化 获取本地存储数据
this.chatarr=JSON.parse(window.localStorage.getItem('chat'))
}
})
</script>
</body>
</html>
演示效果如下
查看浏览器的缓存可以看到如下信息