Vue复习(3)

227 阅读4分钟

5.Vue高级

1.动态组件

  • 动态切换组件的显示与隐藏

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.使用及步骤
  1. 定义要传输的数据
  2. 在具名插槽之后用赋值语句接收到一个对象
  3. 使用{{}}来渲染对象
<!--组件封装者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.工作方式

  1. 用户点击页面上的路由链接
  2. 导致url地址栏中的Hash值发生了变化
  3. 前端路由监听到hash地址的变化
  4. 前端路由把hash地址对应的组件渲染到浏览器中

2.vue-router

1.使用步骤

  1. 安装vue-router的包

    npm install vue-router

  2. 创建路由模块

    1. 在src文件夹下创建router文件夹
    2. router文件夹下新建index.js
  3. 导入并挂载路由模块

    //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")
    
  4. 声明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
    
  5. 声明路由链接和占位符

    <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.嵌套路由

  1. 声明子路由地址
<!--在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>
  1. 编写路由规则和重定向子路由
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的值?

    1. 通过this.$route.params.mid

    2. 通过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.工作原理图

vuex

2.使用步骤

  1. 安装npm i vuex

  2. 在main.js中引入vuex

    import vuex from "vuex"

  3. 使用vuexVue.use(vuex)

  4. 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

  1. 准备数据 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原理图梳理步骤

  1. 组件先调用dispath方法把数据带给action

    <script>
    export default{
    	data(){
            return {
                n:2
            }
        },
        mounted(){
            //1.声明一个函数,调用vuex的dispath方法
            this.$store.dispath("addN",this.n)
        }
    }
    </script>
    
  2. action接收到dispath中的方法和数据并调用

    //2. 调用dispath中的方法
    //action中也可以开启定时器等业务逻辑
    const  actions ={
        //2. 调用dispath中的方法
        addN(context,value){
            //第一个参数中有store身上的一些方法,
           //第二个参数是传递过来的参数this.n
            context.commit("ADDN",value)
        }
    }
    
    
  3. 在action中使用commit把数据交给mutations

    const mutations={
        ADDN(state,value){
            this.sum +=value
        }
    }
    
  4. mutations就可以使用state中的数据

    const mutations={
        ADDN(state,value){
            this.sum +=value
        }
    }
    
  5. 最后把数据渲染回Vuecomponent

    <div>{{$store.state.sum}}</div>