APP.vue
<template>
<div id="app">
<!-- <Singer></Singer>
<Song></Song> -->
<!-- 可以使用v-if来切换Singer和Song
也可以使用动态数组来实现
-->
<component :is="componentId"></component>
<button @click="bool = !bool">修改bool</button>
</div>
</template>
<script>
import Song from './components/song.vue';
import Singer from './components/singer.vue';
export default {
name: 'App',
components: {
Song,
Singer
},
data(){
return{
bool:true,
}
},
computed:{
componentId(){
return this.bool ? "Singer":"Song";
}
},
mounted(){
console.log(this.bool)
}
}
</script>
<style>
</style>
song.vue
<template>
<div class="song">
song:<input type="text"/>
</div>
</template>
<script>
export default{
name:"Song",
}
</script>
singer.vue
<template>
<div class="singer">
singer:<input type="text"/>
</div>
</template>
<script>
export default{
name:"Singer",
}
</script>
缓存动态组件
<!-- 默认缓存所有的动态组件 -->
<keep-alive>
<component :is="componentId"></component>
</keep-alive>
只缓存 Song组件
<!-- 默认缓存所有的动态组件 -->
<keep-alive include="Song">//值是组件的name名
<component :is="componentId"></component>
</keep-alive>
不缓存 Song组件
<!-- 默认缓存所有的动态组件 -->
<keep-alive exclude="Song">//值是组件的name名
<component :is="componentId"></component>
</keep-alive>
router
router/index.js
配置 路由规则
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home'
import About from '@/views/About'
Vue.use(VueRouter)
const routes = [
/// 在这里配置路由规则
]
const router = new VueRouter({
mode: 'history',
routes
})
export default router
main .js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
点击 切换路由的 router-link
app.vue内
<router-link to="/home">跳转home</router-link> |
<router-link to="/about">跳转about</router-link>
配置路由规则
const routes = [
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
路由跳转组件的显示位置
app.vue内
<router-view></router-view>
当我们点击 router-link时 对应的路由组件将会在 router-view 里面出现 展示
$route $router
每个组件 vc上都有 ¥route 里面存储自己的路由信息
整个应用 只有一个$router
一般组件和路由组件的区别
一般组件 通过import引入到父组件中一般放components文件夹内
导入—> 注册 -----> 使用
<template>
<divv class="home">
<h1>我是home组件</h1>
<list></list>
</divv>
</template>
<script>
import list from '@/components/list.vue'
export default{
name:"Home",
components:{
list
}
}
</script>
路由组件 通过 在路由规则中 配置具体的路由 导入组件 展示在router-view位置
一般放在views文件夹内 /pages文件夹内
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home'
import About from '@/views/About'
Vue.use(VueRouter)
const routes = [
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
},
]
const router = new VueRouter({
mode: 'history',
routes
})
export default router
嵌套路由
App.vue
<template>
<div id="app">
<router-link to="/home">跳转home</router-link> <br>
<router-link to="/about">跳转about</router-link>
<hr>
<router-view></router-view>
<hr>
</div>
</template>
<script>
export default {
name: 'App',
components: {
},
data(){
},
computed:{
},
}
</script>
<style>
</style>
home.vue
<template>
<div class="home">
<h1>我是home组件</h1>
<router-link to="/home/singer">歌手</router-link>
<router-link to="/home/song">歌曲</router-link>
<hr/>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Home",
mounted() {
console.log(this)
},
};
</script>
<style lang="sass" scoped></style>
Singer.vue
<template>
<div class="singer">
<ul>
<li v-for="item in singerList" :key="item.id">
<a href="/home/singer/detail">{{ item.name }}</a></li>
</ul>
</div>
</template>
<script>
export default{
name:"Singer",
data(){
return {
singerList:[
{id:"001",name:"歌手1"},
{id:"002",name:"歌手2"},
{id:"003",name:"歌手3"},
{id:"004",name:"歌手4"}
]
}
}
}
</script>
Song.vue
<template>
<div class="song">
<ul>
<li v-for="item in songList" :key="item.id">
<router-link to="/home/song/detail">{{ item.name }}</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default{
name:"Singer",
data(){
return {
songList:[
{id:"001",name:"好汉"},
{id:"002",name:"甜蜜蜜"},
{id:"003",name:"爱你"},
{id:"004",name:"不要"}
]
}
}
}
</script>
detail.vue
<template>
<div class="detail">
编号:<br />
名称:
</div>
</template>
<script>
export default{
name:"Detail",
data(){
return {
}
}
}
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AT4htzlM-1686019007125)(D:\桌面\图片\3.png)]
路由传参
需求:song.vue中点击router-link把对应点击的 对象的id 和name 传给detail组件
song.vue
<ul>
<li v-for="item in songList" :key="item.id">
<router-link to="/home/song/detail?id=001&name='歌手1'">{{ item.name }}</router-link>
</li>
</ul>
上面的写法 不符合需求 因为to 就写死了 没法灵活传递数据
动态绑定属性值 :to
<div class="song">
<ul>
<li v-for="item in songList" :key="item.id">
<router-link
:to="`/home/song/detail?id=${item.id}&name=${item.name}`">{{ item.name }}</router-link>
缺点: 写法混乱 不利于维护
</li>
</ul>
<router-view></router-view>
</div>
detail.vue
<template>
<div class="detail">
编号:{{ $route.query.id }}<br />
名称:{{ $route.query.name }}
</div>
</template>
<script>
export default{
name:"Detail",
data(){
return {
}
},
mounted(){
console.log(this)
}
}
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pCfCIyu1-1686019007126)(D:\桌面\图片\4.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6tkiFvKd-1686019007127)(D:\桌面\图片\5.png)]
方便维护!!!!
<div class="song">
<ul>
<li v-for="item in songList" :key="item.id">
<router-link
:to="{
path:`/home/song/detail`,
query:{
id:item.id,
name:item.name
}
}">
{{ item.name }}</router-link>
</li>
</ul>
<router-view></router-view>
</div>
获取 query
<template>
<div class="detail">
编号:{{ $route.query.id }}<br />
名称:{{ $route.query.name }}
</div>
</template>
<script>
export default{
name:"Detail",
data(){
return {
}
},
mounted(){
console.log(this)
}
}
</script>
路由命名
children:[
{
path:'detail',
name:'detail' ,//可以通过name进行 路由的跳转
// 懒加载
component:()=> import('../views/Detail.vue'),
}
]
Song.vue
<template>
<div class="song">
<ul>
<li v-for="item in songList" :key="item.id">
<router-link
:to="{
// path:`/home/song/detail`,
name:'detail',//只允许对象写法,才能使用name, path和name不能同时使用
query:{
id:item.id,
name:item.name
}
}">
{{ item.name }}</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default{
name:"Song",
data(){
return {
songList:[
{id:"001",name:"好汉"},
{id:"002",name:"甜蜜蜜"},
{id:"003",name:"爱你"},
{id:"004",name:"不要"}
]
}
},
}
</script>
动态路由
传递params参数
children:[
{
path:'detail/:id/:name',
name:'detail' ,//可以通过name进行 路由的跳转
// 懒加载
component:()=> import('../views/Detail.vue'),
}
]
传递参数
<div class="song">
<ul>
<li v-for="item in songList" :key="item.id">
<router-link
to="/home/song/detail/001/歌手1">
{{ item.name }}</router-link>
</li>
</ul>
<router-view></router-view>
</div>
这里写死了
缺点:写法混乱,不利于维护
但是不允许 使用path 只能使用 name
<template>
<div class="song">
<ul>
<li v-for="item in songList" :key="item.id">
<!-- <router-link
:to="`/home/song/detail/${item.id}/${item.name}`">
{{ item.name }}</router-link> -->
<router-link :to="{
name:'detail',
params:{
id:item.id,
name:item.name
}
}"> {{ item.name }}</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JodZRwmI-1686019007127)(D:\桌面\图片\6.png)]
接收params
<template>
<div class="detail">
<!-- 编号:{{ $route.query.id }}<br />
名称:{{ $route.query.name }} -->
编号:{{ $route.params.id }}<br />
名称:{{ $route.params.name }}
</div>
</template>
路由的props配置
把参数处理为props路由组件 可以通过props属性接收
component:()=> import('../views/Song.vue'),
children:[
{
path:'detail',
name:'detail' ,//可以通过name进行 路由的跳转
// 懒加载
component:()=> import('../views/Detail.vue'),
props:{
id:'001',
name:"歌曲1"
}
}
]//数据被写死了
接收 detail组件内
<template>
<div class="detail">
<!-- 编号:{{ $route.query.id }}<br />
名称:{{ $route.query.name }} -->
<!-- 编号:{{ $route.params.id }}<br />
名称:{{ $route.params.name }} -->
编号:{{ id }}<br />
名称:{{ name }}
</div>
</template>
props:["id","name"]
第二个写法
index.js
{
// 访问的时候 实际/home/singer 写·的·时候·不要·写·成·/singer
path:'song',
// 懒加载
component:()=> import('../views/Song.vue'),
children:[
{
path:'detail',
name:'detail' ,//可以通过name进行 路由的跳转
// 懒加载
component:()=> import('../views/Detail.vue'),
// 把路由组件收到的所有的 params参数 以props的形式 传给dejtail组件
props:true
}
]
}
song.vue
<template>
<div class="song">
<ul>
<router-link :to="{
name:'detail',
params:item,
}"> {{ item.name }}</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
接收detail组件内
props:["id","name"]
上面的只能配合params用,不能query用
如果传递的是params 依旧需要 定义动态路由 以及进行传参
props($route){
return{
id:$route.params.id,
name:$route.params.name
}
接收 detail组件内
props:["id","name"]
query形式 依旧需要 传递query参数
<router-link :to="{
name:'detail',
query:item,
}"> {{ item.name }}</router-link>
props:["id","name"]
replace属性
替换 路由记录
默认方式 push(追加历史记录)
replace(替换当前历史记录)
<router-link replace :to="{
name:‘detail’,
query:item,
}"> {{ item.name }}
http://localhost:8081/home
http://localhost:8081/home/song X被替换
http://localhost:8081/home/song/detail?id=004&name=%E4%B8%8D%E8%A6%81
回退 回退到 http://localhost:8081/home
命名视图
同时(同级)展示多个视图 router-view 设置名字 默认default
index.js
{
path: '/about',
name: 'About',
// component: About
// 换成 components 接收{}
components:{
default:About,
pageA:()=>import('../views/Singer.vue'),
pageB:()=>import('../views/Song.vue')
}
},
APP.vue
<template>
<div id="app">
<router-link to="/home">跳转home</router-link> <br>
<router-link to="/about">跳转about</router-link>
<hr>
<!-- 默认展示default -->
<router-view></router-view>
<router-view name="pageA"></router-view>
<router-view name="pageB"></router-view>
<hr>
</div>
</template>
编程式导航
<ul>
<li v-for="item in songList" :key="item.id" @click="toDetail(item)">
{{ item.name }}
</li>
</ul>
methods:{
toDetail(item){
// 跳转路由
this.$router.push(`/home/song/detail?id=${item.id}&name=${item.name}`)
// 第二种写法
this.$router.push({
path:"/home/song/detail",
query:item,
})
}
}
replace除了对历史记录是覆盖替换效果 使用上和push一毛一样
this.$router.replace({
path:"/home/song/detail",
query:item,
})
<button @click="toAbout">前进</button>
methods:{ //历史记录不够用 就跳转失败
toAbout(){
// 在历史中前进n步
this.$router.go(2)
// 在历史中后退n步 负数
this.$router.go(-1)
}
}
this.$router.forward() //前进
this.$router.back() //后退
缓存路由组件
// vue3有变动 vue3再讲
<keep-alive>
<router-view></router-view>
</keep-alive>
include(包含) exclude(不包含)
子级不会被缓存
keep-alive 生命周期
<keep-alive>
<router-view></router-view>
</keep-alive>
被keep-alive缓存的路由组件 当访问路由时 路由组件 激活状态
当切换别的路由时 路由组件 失活状态(没有被销毁)
activated() {
//当组件被激活时
console.log("home组件被激活了");
},
deactivated() {
//当组件失活时
console.log("home组件失活了");
},
nextTick 钩子函数
<div>
<button @click="showIpt">点击显示 input框</button>
<input type="text" v-if="bool" ref="ipt" />
</div>
showIpt() {
this.bool = true;
console.log(this.$refs);
//使得input具有焦点 得等input渲染完毕之后 再执行
//dom更新完毕 调用的钩子函数
this.$nextTick(() => {
this.$refs.ipt.focus();
});
},
路由器的两种 工作模式
// history模式 hash模式 vue3内 稍有变化
const router = new VueRouter({
mode: 'hash',
routes
})
const router = new VueRouter({
mode: 'history',
routes
})
hash模式
http://localhost:8080/#/home/singer
后端 不会把 /#/home/singer 识别为后端 路由 (不会和后端路由发生冲突 )
优点: 1.不会和后端路由发生冲突 2.hash 兼容性更好
缺点: 带#不美观
history
http://localhost:8080/home/singer
优点: 美观 简洁
缺点: 兼容性比hash差 会和后端路由发生冲突 需要后端处理
再次刷新页面 会把 /home/singer 当做后端路由 所以 再次刷新 页面就会丢掉
后端配合可以 解决
比如 node
npm i connect-history-api-fallback
const history = require('connect-history-api-fallback');
app.use(history())
导航守卫
路由匹配前后的 钩子 给用户操作的空间
全局前置守卫
// 全局前置守卫 进入路由之前 先在这里执行 beforeEach
router.beforeEach((to, from, next) => {
//to 去哪个 路由信息对象
//from 来自哪个 路由信息对象
console.log(from);// /
console.log(to); // /about
if (to.meta.isLogin) {
//已登录
next()//执行下一步 放行
} else {
//不放行
next(false)
}
})
全局后置守卫
// 全局后置守卫 路由跳转完成之后触发 afterEach
router.afterEach((to, from) => {
console.log('全局后置钩子');
})
只要有路由的修改 前置和后置钩子就会被触发
独享守卫
写到 路由规则里
{
path: '/home',
name: 'Home',
component: Home,
beforeEnter(to, from, next) {
console.log("/home 独享的的路由守卫 ");
next()//放行
},
alias: ['/home1', '/home2'],
// 定义一些 /home路由的元信息
meta: { isLogin: true },
}
路由组件守卫
beforeRouteEnter(to, from, next) {
//进入路由触发
console.log("detail组件内的 路由进入/detail");
next();
},
beforeRouteLeave(to, from, next) {
//离开路由触发
console.log("detail组件内的 路由离开/detail");
next();
},
beforeRouteUpdate(to, from, next) {
//但是该组件被复用时调用 动态路由时 detail组件不变 数据发生变化 detail组件就是复用的
console.log("detail组件被复用了");
//导航守卫可以访问组件实例 `this`
next();
},
完整的导航解析流程
// 全局前置守卫 进入路由之前 先在这里执行 beforeEach
router.beforeEach((to, from, next) => {
console.log('全局前置钩子(守卫)');
next()
})
// 全局后置守卫 路由跳转完成之后触发 afterEach 唯一不需要带 next
router.afterEach((to, from) => {
console.log('全局后置钩子');
})
{
path: 'detail/:id/:name',
name: 'detail',
component: () => import('../views/Detail.vue'),
props($route) {
return {
id: $route.params.id,
name: $route.params.name
}
},
beforeEnter(to, from, next) {
console.log("/detail 独享的的路由守卫 ");
next()//放行
},
}
组件内
beforeRouteEnter(to, from, next) {
//进入路由触发
console.log("detail组件内的 路由进入/detail");
next();
},
beforeRouteLeave(to, from, next) {
//离开路由触发
console.log("detail组件内的 路由离开/detail");
next();
},
beforeRouteUpdate(to, from, next) {
console.log("detail组件被复用了");
next();
},
当访问某 detail路由
先 全局前置 ==> 独享路由 ==> 组件进入守卫 ==> 全局后置
当离开 detail路由时
先 组件内离开路由 => 全局前置 => 全局后置
当detail 组件复用时
全局前置 ==> 组件复用钩子 ==> 全局后置
})
// 全局后置守卫 路由跳转完成之后触发 afterEach 唯一不需要带 next
router.afterEach((to, from) => {
console.log('全局后置钩子');
})
{
path: 'detail/:id/:name',
name: 'detail',
component: () => import('../views/Detail.vue'),
props($route) {
return {
id: $route.params.id,
name: $route.params.name
}
},
beforeEnter(to, from, next) {
console.log("/detail 独享的的路由守卫 ");
next()//放行
},
}
组件内
beforeRouteEnter(to, from, next) {
//进入路由触发
console.log("detail组件内的 路由进入/detail");
next();
},
beforeRouteLeave(to, from, next) {
//离开路由触发
console.log("detail组件内的 路由离开/detail");
next();
},
beforeRouteUpdate(to, from, next) {
console.log("detail组件被复用了");
next();
},
当访问某 detail路由
先 全局前置 ==> 独享路由 ==> 组件进入守卫 ==> 全局后置
当离开 detail路由时
先 组件内离开路由 => 全局前置 => 全局后置
当detail 组件复用时
全局前置 ==> 组件复用钩子 ==> 全局后置