组件进阶加插槽等知识点

本文深入探讨Vue.js组件的高级特性,包括v-model的语法糖、如何在组件中使用v-model、通过ref和$refs操作DOM、$nextTick的使用、动态组件的概念及其与v-if的区别、自定义指令的创建,特别是全局注册,以及详细讲解了匿名插槽、具名插槽和作用域插槽的用法及其区别。
摘要由CSDN通过智能技术生成

01-v-model语法糖

  • v-model本质上是 value属性和input事件的一层包装

  • v-model的作用:提供数据的双向绑定

    • 数据发生了改变,页面会自动变 v-bind:value

    • 页面输入改变 , 数据会自动变化 v-on:input

  • v-model是语法糖, v-model等价于 给一个input框提供了 :value属性以及 @input事件

很显然如果每次使用input框,都需要提供value和input事件,比较麻烦,所以使用v-model

<template>
  <div>
    <h1>根组件App.vue</h1>
    <!-- 
      1.v-model = "msg"
        (1)data中的数据变化,表单的值也会变化     :value="msg"
        (2)表单的值发生变化,data中的数据也会变化  @input="msg=$event.target.value"
    -->
    <input type="text" v-model="msg" />
    <hr />
    <!-- 这种写法与上面写法功能一致 -->
    <input type="text" :value="msg" @input="msg = $event.target.value" />
    <hr />
    <!-- 这种写法也与上面写法一致 -->
    <input type="text" :value="msg" @input="doInput" />
    <hr />
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: ""
    };
  },
  methods: {
    doInput(e) {
      this.msg = e.target.value;
    }
  }
};
</script>

<style>
</style>

02-组件使用v-model

  • 我们经常遇到一种场景:

    1. 父组件提供一个数据给子组件使用(父传子)

    2. 子组件又需要修改父组件传过来的这个数据,所以需要子传父把值传给父组件。

  • 这种场景可以使用v-model进行简写。

    • 定义组件的时候,注意接收的值叫value, 子传父触发的事件叫 input

    • 如果父传子的props值叫 value, 且 子传父触发的事件叫 input 。 那么这两个功能就可以使用v-model来简写

03-ref和$refs(vue操作dom)

  • ref作用:在vue中操作dom元素或组件vm实例

    • vue不推荐我们直接操作dom。如果真的要在vue中操作dom,可以使用ref语法

      • 说人话 : vue不能直接操作dom,真的要操作也要按vue规定的语法来。(ref语法)

  • 每个 vue 的组件实例上,都包含一个$refs 对象,里面存储着对应的DOM 元素或组件的引用。

ref语法使用流程语法

(1)给标签添加自定义属性red :<button ref="属性名"></button>

  • vue会自动把页面所有的ref属性,挂载到vue实例的$ref对象中

(2)通过 vm.$refs.属性名 获取该标签

  • 一定要注意 : vue在mounted勾子中完成页面真实DOM渲染,所以最早能获取dom的就是mounted钩子

ref易错点

1.添加的的时候是: ref

2.获取的时候是: $refs

1 给需要获取的 dom 元素或者组件, 添加 ref 属性

<template>
  <div>
    <h1>根组件App.vue</h1>
    <div ref="box">我是div盒子</div>
    <Goods ref="goods"></Goods>
    <button @click="fn">点我获取ref</button>
  </div>
</template>

2 通过 this.$refs.xxx 获取, 拿到组件可以调用组件的方法

<script>
//导入子组件
import Goods from "./components/Goods.vue";
export default {
  //注册组件
  components: { Goods },
  //方法
  methods: {
    fn() {
      // 如果ref给dom元素添加,获取的就是dom对象
      console.log(this.$refs.box);//DOM对象
      // 如果ref给组件添加,获取的就是组件vm实例
      console.log(this.$refs.goods);//组件vue实例
      this.$refs.goods.sayHi();
    }
  }
};
</script>

子组件代码Goods.vue

<template>
  <div>
      <h2>我是子组件</h2>
  </div>
</template>

<script>
export default {
    methods: {
        sayHi(){
            console.log('你好我是子组件')
        }
    },
}
</script>

<style>

</style>

04-$nextTick使用

<template>
  <div>
    <!-- 需求1: 点击按钮, 切换显示输入框
         需求2: 显示输入框的同时, 要获取焦点
     -->
    <input type="text" v-if="showInput" ref="inp">
    <button @click="fn" v-else>点此搜索</button>
  </div>
</template>

<script>
export default {
  data () {
    return {
      showInput: false
    }
  },
  methods: {
    fn () {
      // 显示输入框
      this.showInput = true
      // 输入框获取焦点

      /* 报错原因: 
        (1) vue更新DOM是一个异步的过程。(虽然设置了showInput为true,但是不会立即显示输入框)
        (2) 异步代码需要等当前队列同步代码全部执行完毕之后才会执行
        解决方案:
        this.$nextTick(callback) : 会等组件的DOM刷新之后再来执行callback回调函数
      */

      // this.$refs.inp.focus() // 报错
      this.$nextTick( ()=>{
        this.$refs.inp.focus()
      } )
    }
  }
}
</script>

<style>

</style>

05-dynamic动态组件

什么是动态组件: 让多个组件使用同一个挂载点,并动态切换,这就是动态组件。

  • 混淆点解读:动态组件 看起来和v-if v-else功能有些类似,但其实两者是不同的。

    • v-if v-else : 只是根据条件来决定渲染哪一个盒子,不能像组件那样复用。

    • 动态组件:通过设置组件名,让一个挂载点可以切换不同的组件

06-自定义指令

  • 1.添加一个自定义指令 v-focus,作用是让input表单自动聚焦

  • 2.添加一个自定义指令v-color,作用是设置标签文本颜色

<template>
  <div>
    <h1>根组件</h1>
    <!-- 使用指令 v-指令名 -->
    <input type="text" v-focus/>
    <br />
    <p v-color=" bgc ">我是p标签,我使用了自定义指令v-red</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      bgc:'green'
    }
  },
  //自定义指令都写在这个对象里面
  directives: {
    //1.指令名:  focus
    focus: {
      // inserted(el) :  当指令被使用的时候会执行一次
      inserted(el) {
        //el : 你的指令写在哪一个标签上,这个el就是标签dom对象
        el.focus()
      },
      
    },
    //2.指令名: color
    color: {
      inserted(el,binding) {
        console.log(el)//指令所绑定的元素
        console.log(binding)//一个对象,包含指令名、指令值等数据
        console.log(binding.value)//指令值
        el.style.color = binding.value
      },
      // update(el,binding) : 当指令的值发生改变时触发
      update(el,binding) {
        console.log(el,binding)
        el.style.color = binding.value
      },
    }
  }
}
</script>

<style></style>

1.2-自定义指令:全局注册

全局注册: 在main.js中注册,任何地方可用

// 全局指令 - 任何组件内"直接"使用
Vue.directive("focus", {
  inserted(el) {
    el.focus() // 触发标签的事件方法
  }
})

07-slot匿名插槽

插槽使用2个步骤

第一步:在组件中定义一个插槽 <slot>默认值:如果父组件没有传递则默认显示</slot>

第二步:在组件中传递结构: <子组件>父组件需要传递的结构</子组件>

子组件goods.vue

<template>
  <div class="son">
    <h3>我是子组件</h3>
    <h4>商品名称</h4>
    <!-- 插槽:可以让父组件决定这里放什么。 也可以设置默认值 -->
    <slot>我是默认值</slot>
  </div>
</template>

<script>
export default {
  name: "goods",
  data() {
    return {}
  }
}
</script>

<style scoped>
.son {
  border: 1px solid red;
}
</style>

父组件App.vue

<template>
  <div id="app">
    <h1>我是父组件</h1>
    <!-- 子组件1:插入购买链接 -->
    <goods>
      <button>
        <a href="http://www.jd.com">点击购买</a>
      </button>
    </goods>
    <!-- 子组件2:插入禁用点击的按钮 -->
    <goods>
      <button disabled>已卖完</button>
    </goods>
    <!-- 子组件3:没有插入内容,则显示默认插槽 -->
    <goods></goods>
  </div>
</template>

<script>
//导入局部组件
import goods from "./components/goods.vue"
export default {
  data() {
    return {}
  },
  components: {
    goods
  }
}
</script>

<style>
#app {
  border: 1px solid #000;
}
</style>

08-slot具名插槽

  • 1.插槽作用: 父组件 传递html结构 给子组件

    • 给所有slot分发相同内容

  • 2.具名插槽作用: 父组件 传递多个html结构 给子组件

    • 给不同slot分发不同内容

具名插槽语法如下

1.给子组件的<slot>添加name属性 : name="插槽名"

2.父组件使用v-slot:插槽名 : 给指定的插槽传递结构

  • 注意:这个v-slot指令必须要写在<template>标签中,否则会报错

  • <template>是HTML5新增的一个语义化标签,模板的意思。 这个标签本身不会被渲染,因此最终在页面是看不见的。 这个标签类似于div,就是一个空盒子容器。 与div唯一的区别就是它不会渲染。

vue中并不是所有的指令都能简写,有简写符号的指令主要有三个

v-on 指令 可以简写成 @

v-bind指令 可以简写成 :

v-slot指令 可以简写成 #

子组件cell.vue

<template>
  <div class="cell">
      <div class="title" >
          <slot name="a">我是标题</slot>
      </div>
      <div class="content" >
          <slot name="b">我是内容</slot>
      </div>
      <div class="right" >
          <slot name="c">我是图标</slot>
      </div>
  </div>
</template>

<script>
export default {

}
</script>

<style>
    .cell{
        border: 1px solid #f00;
        height: 60px;
        padding: 10px;
        position: relative;
    }

    .title{
        float: left;
        
    }

    .content{
        position: absolute;
        bottom: 10px;
        left: 10px;
    }

    .right{
        float: right;
    }
</style>

父组件

<template>
  <div id="app">
    <h1>我是父组件</h1>
    <cell>
        <template v-slot:a>
            <strong>我是h3</strong>
            <span>标题内容</span>
        </template>
        <template v-slot:b>
            <a href="#">我是链接</a>
        </template>
        <template #c>
            <button>我是按钮</button>
        </template>
    </cell>
  </div>
</template>

<script>
//导入局部组件
import cell from "./components/cell.vue"
export default {
  data() {
    return {}
  },
  components: {
    cell
  }
}
</script>

<style>
#app {
  border: 1px solid #000;
}
</style>

09-v-slot作用域插槽(难点)

1.插槽与props的异同点

相同点: 都是父传子

不同点:

props: 传递的是数据

插槽:传递的是html结构

2.作用域插槽和$emit异同点

相同点:都是子传父

不同点:

$emit : 子传父的数据通过事件来接收

作用域插槽:子传父的数据是通过插槽v-slot接收 (子传父的数据,只能给插槽用)

  • 1.插槽作用:父组件 传递 html结构 给 子组件

  • 2.具名插槽作用:父组件 传递 多个html结构 给 子组件

  • 3.作用域插槽作用:父组件 给 子组件 传递插槽 时,可以使用子组件内部的数据

作用域插槽语法如下

1.给子组件的<slot>添加一个自定义属性 : <slot :属性名="属性值" ></slot>

2.给父组件的<template>添加v-slot属性接收数据: <template v-slot="对象名"></template>

父组件使用子组件内部数据语法: 对象名.属性名

注意点 : 不要把 具名插槽语法作用域插槽 语法搞混淆

具名插槽: <template v-slot:name值></slot>

作用域插槽: <template v-slot="对象名"></slot>

父组件

<template>
  <div>
      <!-- 
      1.匿名插槽 : 父组件传递 一个html结构 给子组件 
        (1)子组件:  <slot> 插槽默认内容 </slot>
        (2)父组件:  <子组件> html结构 </子组件>

      2.具名插槽 : 父组件传递 多个html结构 给子组件
        (1)子组件: 给插槽添加一个name(插槽名)
          <slot name="插槽名"> 插槽默认内容  </slot>
        (2)父组件: 使用 v-slot:插槽名 或  #插槽名
          <子组件> 
            <template v-slot:插槽名>
                html结构
            </template>
          </子组件>

      3.作用域插槽: 子组件传递 数据 给父组件插槽
        (1)子组件 : 给<slot>内置组件添加自定义属性
          <slot 属性名="属性值" > 插槽默认内容 </slot>
        (2)父组件 : 使用 v-slot="对象名"
     -->
     <scope>
       <template v-slot="obj">
         <p>{{ obj.a }}</p>
         <p>{{ obj.b }}</p>
         <p>{{ obj.c }}</p>
       </template>
     </scope>
  </div>
</template>

<script>
//导入局部组件
import scope from './components/scope.vue'
export default {
  //注册组件
  components: {
    scope
  },
  data() {
    return {

    }
  },
  
};
</script>

<style>
#app {
  border: 1px solid #000;
}
</style>

子组件

<template>
  <div class="box">
      <h2>学习作用域插槽</h2>
      <input v-model="msg" type="text" placeholder="输入搜索内容">
      <br>
      <!-- 定义插槽 -->
      <slot a="1" b="2" :c="msg"> 插槽默认内容 </slot>
  </div>
</template>

<script>
export default {
    data() {
        return {
           msg:''
        }
    },
}
</script>

<style scoped>

.box{
    border: 1px solid #000;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值