Vue2.动态组件,插槽,自定义指令

目录

动态组件(component组件)

1. 组件(相当于组件的占位符,有is属性指定渲染组件名字)

2.使用 组件保持动态组件的状态

3.keep-alive 对应的生命周期函数(缓存:deactivated,激活:actived)

4.keep-alive 的 include 属性或exclude属性(指定的name值组件会被缓存)

5.完整动态组件使用方法

插槽(v-slot:指令)

1.插槽的基础用法

2.具名插槽

3.作用域插槽(为预留的 插槽绑定 props 数据)

 3.1可以使用 v-slot: 的形式,接收作用域插槽对外提供的数据,解构插槽 Prop

 4.将App.vue的值传到插槽中,实现父传孙,孙传父(自定义属性,自定义事件)

自定义指令(私有和全局)

1.私有自定义指令(在 directives 节点下声明)

1.2.在使用自定义指令时,需要加上 v- 前缀

1.3.为自定义指令动态绑定参数值 

1.4.通过 binding 获取指令的参数值

1.5.bind函数和update 函数

1.6.bind函数 和update 函数简写

1.7.完整写法

2.全局自定义指令(Vue.directive())


动态组件(component组件

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

1.<component> 组件(相当于组件的占位符,有is属性指定渲染组件名字

vue 提供了一个内置的 <component> 组件,专门用来实现动态组件的渲染。(显示或隐藏)

可以通过is属性的值,表示要渲染的组件的名字,is属性的值,应该是组件在components节点下的注册名称。

App.vue文件:

<template>
  <div class="app-container">
    <h1>App 根组件</h1>
    <hr />
    <button @click="comName='Left'">展示Left</button>
    <button @click="comName='Right'">展示Right</button>
    <hr>
    <div class="box">
      <!-- 渲染 Left 组件和 Right 组件 -->
      <!-- 1.component标签是vue内置的,作用是:组件的占位符 -->
      <!-- 2.is属性的值,表示要渲染的组件的名字 -->
      <!-- 3.is属性的值,应该是组件在components节点下的注册名称 -->
        <component :is="comName"></component>     
    </div>
  </div>
</template>

<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
  components:{
    /* 如果在声明组件的时候,没有为组件指定name名称,则组件名称默认是注册时候的名称 */
    Left,
    Right
  },
  data() {
    return {
      /* comName表示要展示的组件名字 */
      comName:'Left'
    }
  },
}
</script>

2.使用<keep-alive> 组件保持动态组件的状态

默认情况下,切换动态组件时无法保持组件的状态 。此时可以使用 vue 内置的 <keep-alive> 组件保持动态组件的状态。

 keep-alive可以把内部的组件进行缓存,而不是销毁组件

<keep-alive>
        <component :is="comName"></component>     
 </keep-alive>

3.keep-alive 对应的生命周期函数(缓存:deactivated,激活:actived)

  • 当组件被缓存时,会自动触发组件的 deactivated 生命周期函数。
  •  当组件被激活时,会自动触发组件的 activated 生命周期函数。

在组件文件内:

  /* 当组件第一次被创建的时候,既会执行created生命周期,也会执行activated生命周期
  当组件被激活的时候,只会触发actived生命周期,不再触发created,因为组件没有被重新创建*/
  activated(){
    console.log('组件被激活了,activated');
  },
  deactivated(){
    console.log('组件被缓存了,deactivated');
  }

4.keep-alive 的 include 属性或exclude属性(指定的name值组件会被缓存

在使用keep-alive时,其include 属性用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号分隔;  或者使用exclude 属性指定排除项

注意:与include只能两个写一个

App.vue

<keep-alive include="MyLeft">
        <component :is="comName"></component>     
      </keep-alive>

Left.vue

<script>
export default {
  name:'MyLeft',
  /* 当组件第一次被创建的时候,既会执行created生命周期,也会执行activated生命周期
  当组件被激活的时候,只会触发actived生命周期,不再触发created,因为组件没有被重新创建*/
  activated(){
    console.log('组件被激活了,activated');
  },
  deactivated(){
    console.log('组件被缓存了,deactivated');
  }
}
</script>

5.完整动态组件使用方法

App.vue

<template>
  <div class="app-container">
    <h1>App 根组件</h1>
    <hr />
    <button @click="comName='Left'">展示Left</button>
    <button @click="comName='Right'">展示Right</button>
    <hr>
    <div class="box">
      <!-- 渲染 Left 组件和 Right 组件 -->
      <!-- 1.component标签是vue内置的,作用是:组件的占位符 -->
      <!-- 2.is属性的值,表示要渲染的组件的名字 -->
      <!-- 3.is属性的值,应该是组件在components节点下的注册名称 -->

      <!-- keep-alive可以把内部的组件进行缓存,而不是销毁组件 -->
      <!--1.当组件被缓存时,会自动触发组件的 deactivated 生命周期函数。
        2.当组件被激活时,会自动触发组件的 activated 生命周期函数。 -->
        <!-- 注意:与include只能两个写一个 -->
      <keep-alive include="MyLeft">
        <component :is="comName"></component>     
      </keep-alive>
    </div>
  </div>
</template>

<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
  components:{
    /* 如果在声明组件的时候,没有为组件指定name名称,则组件名称默认是注册时候的名称 */
    Left,
    Right
  },
  data() {
    return {
      /* comName表示要展示的组件名字 */
      comName:'Left'
    }
  },
}
</script>

Left.vue

<template>
  <div class="left-container">
    <h3>Left 组件====={{count}}</h3>
    <button @click="count+=1">+1</button>
  </div>
</template>

<script>
export default {
  name:'MyLeft',
  data() {
    return {
      count:0
    }
  },
  created(){
    console.log('Left组件被创建了');
  },
  destroyed(){
    console.log('Left组件被销毁了');
  },
  /* 当组件第一次被创建的时候,既会执行created生命周期,也会执行activated生命周期
  当组件被激活的时候,只会触发actived生命周期,不再触发created,因为组件没有被重新创建*/
  activated(){
    console.log('组件被激活了,activated');
  },
  deactivated(){
    console.log('组件被缓存了,deactivated');
  }
}
</script>

插槽(v-slot:指令)

插槽 Slot )是 vue 为 组件的封装者 提供的能力。允许开发者在封装组件时,把 不确定的 希望由用户指定的部分 定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的 内容的占位符

1.插槽的基础用法

  • 在封装组件时,可以通过 <slot> 元素定义插槽,从而为用户预留内容占位符
  • 如果在封装组件时没有预留任何 <slot> 插槽,则用户提供的任何自定义内容会被丢弃
  • 封装组件时,可以为预留的 <slot> 插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何 内容,则后备内容会生效。
  • 在使用组件的组件上,v-slot:指令不能直接用在元素身上,必须用在template标签 ,template标签是一个虚拟的标签,只起到包裹性质的作用,不会被渲染为任何实质性的html元素
  • v-slot:指令的简写形式是#
  • v-slot:后面要跟上插槽的名字

被使用的组件:

    <!-- 声明一个插槽区域 -->
    <!-- vue官方规定:每一个slot插槽,都有一个name名称 -->
    <!-- 如果省略slot的name属性,则有一个默认名称叫做default -->
    <slot name="default">
      <h6>这是default插槽默认的默认内容</h6>
    </slot>

使用组件的组件:

      <Left>
        <!-- 默认情况下,在使用组件的时候,
        提供的内容都会被填充到名字为default的slot插槽中 -->
        <!-- v-slot:指令的简写形式是# -->
        <template #default>
          <p>这是在Left组件的内容区域,声明的p标签</p>
        </template>
      </Left>

2.具名插槽

如果在封装组件时 需要预留多个插槽节点 ,则需要为每个 <slot> 插槽指定 具体的 name 名称 。这种 带有具体 名称的插槽 叫做“具名插槽”。
被使用的组件:
<!-- 具名插槽 -->
 <slot name="title"></slot>

使用组件的组件: 

     <!-- 具名插槽 -->
      <template #title>
        <h3>迪丽热巴</h3>
      </template>

3.作用域插槽(为预留的 <slot> 插槽绑定 props 数据

在封装组件的过程中,可以为预留的 <slot> 插槽绑定 props 数据,这种 带有 props 数据的 <slot> 叫做“ 作用 域插槽 ”。

 3.1可以使用 v-slot: 的形式,接收作用域插槽对外提供的数据,解构插槽 Prop

  •  可以使用 v-slot: 的形式,接收作用域插槽对外提供的数据
  • 作用域插槽对外提供的数据对象,可以使用解构赋值简化数据的接收过程。

被使用的组件:

<template>
  <div class="article-container">
      <h3>Article组件</h3>

      <!-- 文章的内容 -->
      <div class="content-box">
          <!-- msg是自定义属性,可以通过这传给使用该插槽的组件,相当于子传父,作用域插槽 -->
          <!-- 在封装组件时,为预留的slot提供属性对应的值,这种做法叫“作用域插槽” -->
         <slot name="content" msg="hello vue.js"  :user="userinfo"></slot>
      </div>
  </div>
</template>

<script>
export default {
    data() {
        return {
            /* 用户对象 */
            userinfo:{
                name:'dilireba', 
                age:20
            },      
        }
    },
}
</script>

使用组件的组件:

<template>
  <div class="app-container">
      <!-- 解构赋值{ msg } -->
      <template #content="{ msg, user }">
        <div>
          <p>迪丽热巴迪丽热巴迪丽热巴迪丽热巴迪丽热巴迪丽热巴</p>
          <p>迪丽热巴迪丽热巴迪丽热巴迪丽热巴迪丽热巴迪丽热巴</p>
          <p>迪丽热巴迪丽热巴迪丽热巴迪丽热巴迪丽热巴迪丽热巴</p>
          <p>{{ msg }}</p>
          <p>{{ user.name }}</p>
        </div>
      </template>
    </Article>

    <hr />
  </div>
</template>

<script>
import Article from "@/components/Article.vue";
export default {
  components: {
    Article,
  }
}
</script>

 4.将App.vue的值传到插槽中,实现父传孙,孙传父(自定义属性,自定义事件)

子组件Goods.vue

<template>
  <div class="goods-container">
        <!-- 插槽-->
        <slot></slot>
  </div>
</template>

App.vue

<template>
  <div class="app-container">
   <Goods v-for="item in list" :key="item.id">
    <!-- 自定义属性和自定义事件 -->
    <Counter :num="item.goods_count" @num-change="getNewNum(item,$event)"></Counter>
   </Goods>
  </div>
</template>

<script>
// 导入 axios 请求库
import axios from 'axios'
// 导入需要的组件
import Goods from '@/components/Goods/Goods.vue'
import Counter from './components/Counter/Counter.vue'

export default {
  data() {
    return {
      // 用来存储购物车的列表数据,默认为空数组
      list: []
    }
  },
  created() {
    // 调用请求数据的方法
    this.initCartList()
  },
  methods: {
    // 封装请求列表数据的方法
    async initCartList() {
      // 调用 axios 的 get 方法,请求列表数据
      const { data: res } = await axios.get('https://www.escook.cn/api/cart')
      // 只要请求回来的数据,在页面渲染期间要用到,则必须转存到 data 中
      if (res.status === 200) {
        this.list = res.list
      }
    },
    /* 获取Counter组件发过来的最新数量值 */
    getNewNum(item,val){
      // console.log(item,val);
      item.goods_count=val
    }
  },
  components: {
    Goods,
   Counter
  }
}
</script>

孙组件Counter.vue

<template>
  <div class="number-container d-flex justify-content-center align-items-center">
    <!-- 减 1 的按钮 -->
    <button type="button" class="btn btn-light btn-sm" @click="sub">-</button>
    <!-- 购买的数量 -->
    <span class="number-box">{{ num }}</span>
    <!-- 加 1 的按钮 -->
    <button type="button" class="btn btn-light btn-sm" @click="add">+</button>
  </div>
</template>

<script>
export default {
  props:{
    /* 要展示的商品数量 */
    num:{
      type:Number,
      default:1
    }
  },
  methods: {
    add(){
      /* 通过自定义事件的方法把最新值发给父组件 */
      this.$emit('num-change',this.num+1)
    },
    sub(){
      if(this.num-1===0) return
       /* 通过自定义事件的方法把最新值发给父组件 */
      this.$emit('num-change',this.num-1)
    }
  },
}
</script>

自定义指令(私有和全局)

vue 官方提供了 v-text、v-for、v-model、v-if 等常用的指令。除此之外 vue 还允许开发者自定义指令。
vue 中的自定义指令分为两类,分别是:
  • 私有自定义指令
  • 全局自定义指令

1.私有自定义指令(directives 节点下声明

1.2.在使用自定义指令时,需要加上 v- 前缀

1.3.为自定义指令动态绑定参数值 

在 template 结构中 使用自定义指令 时,可以通过等号( = )的方式,为当前指令 动态绑定参数值,会去data里面找值
    <!-- v-color=“”会去data中找值 -->
    <h1 v-color="color">App 根组件</h1>
    <p v-color="'blue'">迪丽热巴</p>

1.4.通过 binding 获取指令的参数值

在声明自定义指令时,可以通过形参中的 第二个参数 ,来接收指令的参数值

1.5.bind函数和update 函数

  • 当指令第一次被绑定到元素上的时候,会立即触发bind函数
  • 每次DOM更新时被调用,会触发update函数
  • 形参中的el表示当前指令所绑定的那个DOM对象

bind 函数 只调用 1 次 :当指令第一次绑定到元素时调用, 当 DOM 更新时 bind 函数不会被触发 update 函 数会在 每次 DOM 更新时 被调用。
  /* 私有自定义指令节点directives */
  directives: {
    // 定义名为color的指令,指向一个配置对象
        color:{
      //当指令第一次被绑定到元素上的时候,会立即触发bind函数
      //形参中的el表示当前指令所绑定的那个DOM对象
      // 通过binding对象的.value属性,获取动态的参数值
      bind(el,binding){
        console.log('触发了v-color的bind函数');
        console.log(binding);
        el.style.color=binding.value
      },
      //每次DOM更新时被调用,会触发update函数
      update(el,binding){
        console.log('触发了v-color的update函数');
        el.style.color=binding.value
      }
    }

1.6.bind函数 update 函数简写

    /* 如果bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式 */
    color(el, binding) {
      el.style.color = binding.value;
    },

1.7.完整写法

<template>
  <div class="app-container">
    <!-- v-color=“”会去data中找值 -->
    <h1 v-color="color">App 根组件</h1>
    <p v-color="'blue'">迪丽热巴</p>
    <button @click="color = 'red'">改变color的颜色值</button>

   <hr />
  </div>
</template>

<script>
export default {
  data() {
    return {
      color: "orange",
    };
  },
  /* 私有自定义指令节点directives */
  directives: {
    // 定义名为color的指令,指向一个配置对象
    /*     color:{
      //当指令第一次被绑定到元素上的时候,
     //会立即触发bind函数形参中的el表示当前指令所绑定的那个DOM对象 
      bind(el,binding){
        console.log('触发了v-color的bind函数');
        console.log(binding);
        el.style.color=binding.value
      },
      //每次DOM更新时被调用,会触发update函数
      update(el,binding){
        console.log('触发了v-color的update函数');
        el.style.color=binding.value
      }
    } */

    /* 如果bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式 */
    color(el, binding) {
      el.style.color = binding.value;
    }
  },
};
</script>

2.全局自定义指令(Vue.directive()

全局共享的自定义指令需要通过“ Vue.directive() ”进行声明,类似全局过滤器
在main.js入口函数:
import Vue from 'vue'
import App from './App.vue'

/* 在终端提示打包发布 */
Vue.config.productionTip = false

/* 全局自定义指令 */
/* Vue.directive('color', {
    bind(el, binding) {
        el.style.color = binding.value
    },
    update(el, binding) {
        el.style.color = binding.value
    }
}) */
Vue.directive('color', function(el, binding) {
    el.style.color = binding.value
})

/* 全局过滤器 */
// Vue.filter('过滤器名字', function() {})


new Vue({
    render: h => h(App),
}).$mount('#app')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值