5.Vue高级
1.动态组件
- 动态切换组件的显示与隐藏
1.实现步骤
- 在components标签的is属性绑定要展示的组件
2.使用
<template>
<div>
<button>点击展示Left组件</button>
<button>点击展示Right组件</button>
<component :is ="active"></component>
</div>
</template>
<script>
import Left from "./components/Left"
import Right from "./components/Right"
export default {
component:{
Left,
Right
},
data(){
return {
active:"Left"
}
},
methods:{
showLeft(){
this.active="Left"
},
showRight(){
this.active="Right"
}
}
}
</script>
2.keep-alive
1.使用
-
切换组件的时候组默认会被销毁
-
keeep-alive可以保持组件状态不会被销毁
<template>
<div>
<button>点击展示Left组件</button>
<button>点击展示Right组件</button>
<keep-alive>
<component :is ="active"></component>
</keep-alive>
</div>
</template>
2. 生命周期
- 当组件被缓存的时候,会自动触发deactivated生命周期函数
- 当组件被激活的时候,会自动触发activated生命周期函数
- 当组件第一次被创建的时候既会触发created也会触发activate
- 当组件被激活的时候只会触发activate
3.选择性缓存
- 给keep-alive添加include属性可以 选择哪些组件可以被缓存
- exclude是指定哪些组件不被缓存,和include相对
<template>
<div>
<button>点击展示Left组件</button>
<button>点击展示Right组件</button>
<keep-alive include="Left,Right">
<component :is ="active"></component>
</keep-alive>
</div>
</template>
4.组件的名称
- 如果在声明组件的时候没有指定名称,则组件默认就是注册的时候的名称
- 当提供了name名称之后,组件的名称就是name属性的值
- keep-alive以name属性的名称为主
2.插槽
- 插槽是封装组件的时候把不确定的,希望由用户指定的部分定义为插槽
- 不使用插槽内容默认会被丢弃
1.使用步骤
<!--组件封装者Left-->
<template>
<div>
<h3>hello slot</h3>
<slot></slot>
</div>
</template>
<!--组件使用者-->
<template>
<div>
<Left>
<p>hello vue</p>
</Left>
</div>
</template>
2.注意
- 每一个插槽都要有一个名称
- 如果忽略了name,默认叫做default
3.v-slot
- 如果需要指定内容默认放在哪一个插槽中,需要给插槽加name属性
- 使用的时候用v-slot绑定name值
- 默认v-slot只能加在template标签上
- v-slot可以简写为#
- 可以在定义插槽的时候指定默认内容
- v-slot如果有名字就叫做具名插槽
<!--组件封装者Left-->
<template>
<div>
<h3>hello slot</h3>
<slot name="content">我是默认内容,使用者不使用是我将展示</slot>
</div>
</template>
<!--组件使用者-->
<template>
<div>
<Left>
<template v-slot:content>
<p>hello vue</p>
</template>
</Left>
</div>
</template>
4.作用域插槽
- 在封装组件的时候,为预留的插槽提供属性对应的值叫做作用域插槽
- 可以使用插槽传输数据
1.使用及步骤
- 定义要传输的数据
- 在具名插槽之后用赋值语句接收到一个对象
- 使用{{}}来渲染对象
<!--组件封装者Left-->
<template>
<div>
<h3>hello slot</h3>
<slot name="content" msg="hello vue">我是默认内容,使用者不使用是我将展示</slot>
</div>
</template>
<!--组件使用者-->
<template>
<div>
<Left>
<template #content="scope">
<p>hello vue</p>
<p>{{scope.msg}}</p>
</template>
</Left>
</div>
</template>
-
使用作用域插槽的时候可以同时使用解构赋值
<template #content="{msg,user}"> <p>hello vue</p> <p>{{msg,user}}</p> </template>
3.自定义指令
1.私有自定义指令
- 使用v-color解析时,引号后面默认会当作js表达式
- bind函数第一个参数是DOM节点
- 第二个参数是使用v-color时传递过来的值
- bind函数只会在第一次绑定的时候会被调用
- 当页面更新的时候bind函数不会再次调用,需要用到updata函数
- 如果写成函数形式,color在第一次绑定的时候会调用,在更新的时候也会调用
<!--定义自定义指令-->
<script>
export default {
directives:{
color:{
bind(el,binding){
el.style.color=binding.value
},
update(el,binding){
el.style.color=binding.value
},
}
}
}
</script>
<template #content="{msg,user}">
<div>
<p v-color="color">
</p>
</div>
</template>
2.全局自定义指令
//main.js
Vue.directive("color",function(el,binding){
el.style.color = binding.value
})
6.前端路由
- 路由指的是地址与组件的对应关系
1.工作方式
- 用户点击页面上的路由链接
- 导致url地址栏中的Hash值发生了变化
- 前端路由监听到hash地址的变化
- 前端路由把hash地址对应的组件渲染到浏览器中
2.vue-router
1.使用步骤
-
安装vue-router的包
npm install vue-router -
创建路由模块
- 在src文件夹下创建router文件夹
- router文件夹下新建index.js
-
导入并挂载路由模块
//router文件夹下新建index.js就是路由模块 //1.导入Vue和vue-router的包 import Vue from "vue" import VueRouter from "vue-router" //调用vue.use函数,把router作为vue的插件 Vue.use(VueRouter) //3.创建路由实例对象 const router = new VueRouter() //4.向外共享实例对象 export default router//main.js中需要挂载路由实例对象 import Vue from "vue" import VueRouter from "vue-router" import router from "./src/router/index.js" new Vue({ render:h=>(App), //挂载路由实例对象 router }).$mount("#app") -
声明hash地址与组件之间的对应关系
import Vue from "vue" import VueRouter from "vue-router" import Home from "./home.vue" import Movie from "./movie.vue" import About from "./about.vue" Vue.use(VueRouter) const router = new VueRouter({ //需要在routes节点下定义hash地址与组件之间的对应关系 routes:[ {path:"/home",component:Home}, {path:"/movie",component:Moive}, {path:"/about",component:About} ] }) export default router -
声明路由链接和占位符
<template> <div> <!--router-link编译的时候就会编译为a标签--> <router-link to="/home">首页</router-link> <router-link to="/movie">电影</router-link> <router-link to="/about">关于</router-link> <!--router-view是占位作用,是路由的展示区域--> <router-view></router-view> </div> </template>
-
router-link的replace工作模式
<template> <div> <router-link to="/about" r >关于</router-link> </div> </template>
2.路由基本规则
1. 重定向
import Vue from "vue"
import VueRouter from "vue-router"
import Home from "./home.vue"
import Movie from "./movie.vue"
import About from "./about.vue"
Vue.use(VueRouter)
const router = new VueRouter({
//需要在routes节点下定义hash地址与组件之间的对应关系
routes:[
{path:"/",redirect:"/home"},
{path:"/home",component:Home},
{path:"/movie",component:Moive},
{path:"/about",component:About}
]
})
3.嵌套路由
- 声明子路由地址
<!--在about组件中-->
<template>
<div>
<router-link to="/about/tab1">tab1</router-link>
<router-link to="/about/tab2">tab2</router-link>
<router-view></router-view>
</div>
</template>
- 编写路由规则和重定向子路由
import tab1 from "./tab1.vue"
import tab2 from "./tab2.vue"
const router = new VueRouter({
//需要在routes节点下定义hash地址与组件之间的对应关系
routes:[
{
path:"/about",
component:About,
//重定向子路由
redirect:"/about/tab1"
children:[
{path:"tab1",component:tab1},
{path:"tab2",component:tab2},
]
}
]
})
2. 默认子路由
- 如果children数组中,某个路由规则的path是空字符串,这条规则就是默认子路由
- 和重定向效果一样
const router = new VueRouter({
//需要在routes节点下定义hash地址与组件之间的对应关系
routes:[
{
path:"/about",
component:About,
children:[
{path:"",component:tab1},
{path:"tab2",component:tab2},
]
}
]
})
3. 动态路由
- 当要展示某条数据的具体信息就需要用id给路由传递id属性
- 把hash地址中可变的部分定义为参数项
<template>
<div>
<router-link to="/movie/1">电影1</router-link>
<router-link to="/movie/2">电影2</router-link>
<router-link to="/movie/3">电影3</router-link>
<router-view></router-view>
</div>
</template>
const router = new VueRouter({
routes:[
{path:"/movie/:mid",component:Movie},
]
})
-
如何在Movie组件中拿到mid的值?
-
通过
this.$route.params.mid -
通过props传参
//定义路由规则 const router = new VueRouter({ routes:[ //开启props传参 {path:"/movie/:mid",component:Movie,props:true}, ] })<!--movie 组件--> <template> <div> {{mid}} </div> </template> <script> export default{ //接收路由规则里参数的值 props:["mid"] } </script>
-
-
this.$rout是参数对象
-
this.$router是导航对象
4.导航
1.声明式导航
- 在浏览器中,点击链接实现导航的方式,叫做声明式导航
- a链接和routre-link都属于声明式导航
2.编程式导航
-
在浏览器中,调用API实现导航的方式叫做编程式导航
-
location.href -
在vue中使用的导航方法有
this.$router.push("hash地址")- 跳转到指定地址,并增加一条历史记录
this.$router.replace("hash地址")- 跳转到指定地址,不会增加历史记录
this.$router.go(n)- go(-1)
- 如果后退层数超过上限,则原地不动
5.导航守卫
1.全局前置守卫
- 每次发生路由的导航跳转时,都会触发全局前置守卫。
- 对每个路由进行访问权限的控制
//在路由配置规则中添加全局前置守卫
//创建路由实例对象
const router = new VueRouter({})
//调用路由 实例对象的beforEach
//每次页面发生跳转的时候都会触发fn回调函数
router.beforeEach(fn)
1.关于前置守卫的回调函数
- 需要使用
router.beforeEach(()=>{})声明导航守卫 - 每次页面发生跳转的时候都会触发fn回调函数
- 参数作用
to是将要访问的路由的信息对象from是将要离开的路由信息对象next是一个函数,调用next就表示放行,允许这次导航
//src/router/index.js
router.beforeEach((to,from,next)=>{
//判断hash地址是否为main
//如果是main就需要登录
if(to.path=="/main"){
//读取token
const token = localStorage.getItem("token")
//有token放行
if(token){
next()
//无token强制跳转到登录页
}else{
next("/login")
}
//访问的不是main,直接放行
}else{
next()
}
})
//src/router/index.js
const router = new VueRouter({
//需要在routes节点下定义hash地址与组件之间的对应关系
routes:[
{
path:"/about",
component:About,
//添加自定义配置,可以用于路由的判断
meta:{
isAuth:false
}
}
]
})
//src/router/index.js
router.beforeEach((to,from,next)=>{
//判断是否需要路由守卫
if(!to.meta.isAuth){
const token = localStorage.getItem("token")
if(token){
next()
//无token强制跳转到登录页
}else{
next("/login")
}
//访问的不是main,直接放行
}else{
next()
}
})
2.全局后置守卫
- 每次切换之后调用
- 对切换后的路由进行组件的修改操作
//src/router/index.js
router.afterEach((to,from)=>{
document.title = to.meta.title || "硅谷"
})
3.独享后置守卫
- 在路由规则中直接配置
//src/router/index.js
import Vue from "vue"
import VueRouter from "vue-router"
import Home from "./home.vue"
import Movie from "./movie.vue"
import About from "./about.vue"
Vue.use(VueRouter)
const router = new VueRouter({
//需要在routes节点下定义hash地址与组件之间的对应关系
routes:[
{
path:"/home",
component:Home,
beforeEnter=(to,from,next)=>{
//业务逻辑和前置路由守卫一样
}
},
]
})
export default router
4. 组件内路由守卫
<template>
<div>
<router-link to="/movie/1">电影1</router-link>
<router-link to="/movie/2">电影2</router-link>
<router-link to="/movie/3">电影3</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
//通过路由规则进入路由组件时被调用
beforeRouteEnter(to,from,next){
},
//通过路由规则,离开该组件的时候被调用
beforeRouteLeave(to,from,next){
}
}
</script>
5. 其他配置
1.更改工作模式
- 默认是hash模式
- hash模式好处就是不用拿着#后面的地址请求服务器
- 兼容性好
- history模式
- 不能刷新页面
- 刷新页面之后要重新请求服务器
//src/router/index.js
import Vue from "vue"
import VueRouter from "vue-router"
Vue.use(VueRouter)
const router = new VueRouter({
//更改工作模式
mode:"history" //hash工作模式
routes:[
{
path:"/home",
component:Home,
beforeEnter=(to,from,next)=>{
}
},
]
})
export default router
7.vuex?
- 专门在vue中实现集中式状态(数据)管理的一个vue插件,
- 对vue应用中多个组件共享的状态进行集中式管理,
- 是组件通信的方式,且适用于任意组件间通信
- 适用于
- 多个组件依赖于同一个数据
- 来自于不同组件行为需要变更同一状态
1.工作原理图
2.使用步骤
-
安装
npm i vuex -
在main.js中引入vuex
import vuex from "vuex" -
使用vuex
Vue.use(vuex) -
在
src下创建store文件夹,此文件夹下创建index.js
// store/index.js
import vuex from "vuex"
import Vue from "vue"
//准备actions————用于响应组件中的动作
const actions ={}
//准备mutations----用于操作数据
const mutations={}
//准备state---用于存储数据
const state = {}
Vue.use(vuex)
//创建并暴露store
export default new Vue.Store({
actions,
mutations,
state,
})
//main.js
import Vue from "vue"
//引入store
import store from "./src/store"
new Vue({
el:"#app",
render:h=>h(App),
store,
})
import语句会在提升至代码最上方
3.组件使用vuex
- 准备数据
const state = {num:0}
// store/index.js
import vuex from "vuex"
import Vue from "vue"
const actions ={
//2. 调用dispath中的方法
addN(context,value){
//第一个参数中有store身上的一些方法,第二个参数是传递过来的参数this.n
context.commit("ADDN",value)
}
}
const mutations={
ADDN(state,value){
this.sum +=value
}
}
const state = {
num:0,
}
Vue.use(vuex)
export default new Vue.Store({
actions,
mutations,
state,
})
<template>
<div>{{$store.state.sum}}</div>
<button @click="addn">点击调用add</button>
</template>
<script>
export default{
data(){
return {
n:2
}
},
methods:{
add(){
//1.声明一个函数,调用vuex的dispath方法
this.$store.dispath("addN",this.n)
}
}
}
</script>
2.根据vuex原理图梳理步骤
-
组件先调用dispath方法把数据带给action
<script> export default{ data(){ return { n:2 } }, mounted(){ //1.声明一个函数,调用vuex的dispath方法 this.$store.dispath("addN",this.n) } } </script> -
action接收到dispath中的方法和数据并调用
//2. 调用dispath中的方法 //action中也可以开启定时器等业务逻辑 const actions ={ //2. 调用dispath中的方法 addN(context,value){ //第一个参数中有store身上的一些方法, //第二个参数是传递过来的参数this.n context.commit("ADDN",value) } } -
在action中使用commit把数据交给mutations
const mutations={ ADDN(state,value){ this.sum +=value } } -
mutations就可以使用state中的数据
const mutations={ ADDN(state,value){ this.sum +=value } } -
最后把数据渲染回Vuecomponent
<div>{{$store.state.sum}}</div>