前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >懂个锤子Vue 自定义指定、插槽:

懂个锤子Vue 自定义指定、插槽:

原创
作者头像
Java_慈祥
发布2024-07-31 18:40:42
1080
发布2024-07-31 18:40:42
举报
文章被收录于专栏:Web前后端、全栈出发

Vue自定义指定、插槽🛠️:

前言:当然既然学习框架的了,HTML+CSS+JS三件套必须的就不说了: JavaScript 快速入门

紧跟前文,目标学习Vue2.0——3.0: 懂个锤子VueWebPack5.0WebPack高级进阶 涉及的技术栈…

学习前置链接: 懂个锤子Vue 项目工程化 懂个锤子Vue 项目工程化进阶⏫

自定义指令:

内置指令:Vue.JS提供的一组内置的功能指令,它们以v- 前缀开始:v-text\v-bind\v-if

这些指令使得开发者能够以声明式的方式实现数据与视图的绑定,从而简化了DOM操作;

自定义指令:是Vue.js框架中的一个核心特性,它允许开发者扩展Vue的模板语言:

实现对DOM元素的定制化操作,这种机制为开发者提供了直接操作控制DOM能力;

从而在不深入组件内部逻辑的情况下,增加或修改元素的行为;

自定义指令分为: 全局注册、局部注册;

全局自定义指令:

全局注册: 在Vue中让指令在整个应用程序中可用的方法,通过调用Vue.directive方法完成;

Vue的静态方法 Vue.directive(id, definition) 它接受两个参数:

指令名称:不需加v-前缀,Vue会自动添加,使用时需要加:v-指令名称

定义对象:该对象内包含指令执行的生命周期钩子函数;

  • bind: 指令第一次绑定到元素时触发
  • inserted: 元素被插入到父节点时触发
  • update: 组件内的数据变化导致VNode更新时触发
  • componentUpdated: 组件及子组件更新完成后触发
  • unbind: 指令从元素上解除绑定时触发

定义对象:内置生命钩子函数参数:

  • el: 指令所绑定的DOM元素,这使得你可以在指令的逻辑中直接操作DOM,比如:添加样式、修改属性或触发事件;
  • binding: 包含指令详细信息对象:name指令的名字、value绑定到指令的值、expressionargmodifiers等属性;
  • vnode 和 oldVnode: Vue编译后的虚拟节点,用于更复杂的操作;

vnode: Vue使用虚拟DOM(VNode)来表示真实的DOM元素,

oldVnode: 在更新过程中,oldVnode提供了更新前的虚拟节点状态

代码语言:js
复制
//main.JS 中定义全局指令:
Vue.directive('自定义指令名称', {
  bind(el, binding, vnode) { /** 初始化操作 */ },
  inserted(el, binding, vnode) { /** 元素插入DOM时的操作 */ },
  update(el, binding, vnode, oldVnode) { /** 数据更新时的操作 */  },
  componentUpdated(el, binding, vnode, oldVnode) { /** 组件及子组件更新后的操作 */ },
  unbind(el, binding, vnode) { /** 解绑前的操作 */ }
});

注意: 全局注册需要定义在main.JS 中,或: 单独定义在JS文件中,main.js 引入并调用:

代码语言:js
复制
import Directives from './JS/directives'			//自定义全局指令, JS文件目录;
import Vue from 'vue'
Vue.use(Directives);

全局注册自定义指令在Vue应用中可以简化和复用复杂的DOM操作逻辑,以下是一些应用场景: main.js

自动聚焦:创建一个v-focus指令,使元素在插入页面时自动获取焦点;

代码语言:js
复制
//全局注册指令
Vue.directive('focus', {
  //inserted 会在指令所在的元素,被插入到页面中时触发
  inserted (el) {
    // el 就是指令所绑定的元素
    // console.log(el);
    el.focus()
  }
})

局部自定义指令:

局部注册:Vue.js中一种:更加模块化和灵活的方式,为特定组件添加定制的DOM行为;

与全局注册不同: 局部指令仅在定义它的组件及其子组件中可用,这有助于减少全局命名空间的污染;

自定义指令语法,与全局类似: 在Vue2.x、3.x中,局部指令注册,通常在单文件组件script部分,常规的Vue组件中进行;

代码语言:js
复制
export default {
  name: 'App',
  //在Vue组件directives配置项中定义
  directives: {
    //自定义指令名:{ 指令的配置项 }
    myfocus: {
      inserted (el) {
        el.focus(); //对el标签: 扩展额外功能;
      }
    }
  }
}

自定义指令—传值:

需求: 实现一个 v-color指令 - 传入不同的颜色,给标签设置文字颜色;

代码语言:html
复制
<template>
  <div id="app">
    <input type="text" v-model="color" >
    <h1 v-color="color" >局部自定义指令</h1>    <!-- 设置v-color值来自: 输入框,同步 -->
  </div>
</template>

<script>
export default {
  name: 'App',
  //默认color 颜色: 红色;
  data(){ return { color:"red" } },
  //在Vue组件directives配置项中定义
  directives: {
    //指令名:指令的配置项
    //对el标签: 扩展额外功能;
    myfocus: { inserted (el) { el.focus(); } },
    color: {
      //inserted 提供的是元素被添加到页面中时的逻辑
      inserted (el, binding) { el.style.color = binding.value },
      //update 指令的值修改的时候触发,提供值变化后,dom更新的逻辑
      update (el, binding) { el.style.color = binding.value }
    }
  }
}
</script>
<style></style>

插槽:

Vue.js中的插槽Slot 是组件化开发中的一个核心特性:

它允许在 父组件 ——中向—— 子组件 传递和控制渲染的内容,从而实现更加灵活和复用的组件设计;

举例Demo: 我们经常遇到:引入组件模块样式、结构相同,但组件内容数据不同的情况,当然这也可以通过:父子传参解决

插槽Slot

插槽Slot 是一种内容分发机制:

使得父组件可以将特定的HTML内容插入到子组件的特定位置,

这使得子组件的结构更加灵活,能够适应不同的内容需求,而不需要修改子组件的内部实现;

为什么需要插槽,不能通过其他方式来解决吗,父子组件通信也可以解决啊🙂:

  • 内容的灵活性:

没有插槽的情况下,如果想要在子组件中显示不同的内容,通常需要将这些内容硬编码到子组件模板中;

或者,通过属性传递数据,但这限制了父组件对子组件内部结构的控制;

  • 数据与结构的解耦:

直接通过属性传递数据并控制结构,可能会导致数据和展示逻辑紧密耦合,不便于维护和扩展

作用域插槽不仅传递数据,还允许父组件控制如何展示这些数据,

实现了数据和展示逻辑的分离,提高了代码的可维护性和可读性

  • 组件的封装性与复用性:

如果每个个性化需求都要求修改子组件,这会破坏组件的封装性,使得维护变得困难;

通过插槽,子组件可以保持其核心功能和结构不变,同时允许外部内容插入,这样既保持了组件的纯净,又实现了复用;

总结: 插槽本质实现的功能和父子组件通信类似,但对于组件需要大量的数据修改,且伴随结构改变判断,单纯组件实现就会

变得麻烦,插槽定义了明确的填充区域,使得组件的使用方式更加直观,提高了代码的可读性和可维护性;

定义插槽:

应用场景: 实现上述案例,组件警告弹框提示⚠️,插槽修改其内部数据;

插槽基本语法:

  • 组件内需要定制的结构部分,改用<slot></slot>占位;
  • 使用组件时, <组件>替换插槽内容</组件>标签内部, 传入结构替换slot

./components/MyDialog.vue: 自定义组件插槽,警告弹框提示:内容由<slot> 占位,父组件定制传入;

代码语言:html
复制
<template>
  <div class="dialog">
    <div class="dialog-header">
      <h3>警告:</h3>
      <span class="close">✖️</span>
    </div>
    <div class="dialog-content">
      <!-- 在需要定制的位置,使用slot占位 -->
      <slot>插值:默认显示内容;</slot>
    </div>
    <div class="dialog-footer">
      <button>取消</button>
      <button>确认</button>
    </div>
  </div>
</template>

<script>
  export default { data () { return {  } } }
</script>
<!-- 省略样式代码 -->
<style scoped></style>

App.vue: 主组件调用——>插槽组件并传值,运行查看效果;

代码语言:html
复制
<template>
  <div id="app">
    <!-- 外部使用组件时,不传东西,则slot会显示后备内容 -->
    <MyDialog></MyDialog>
    <!-- 外部使用组件时,传东西了,则slot整体会被换掉 -->
    <MyDialog>组件调用完成,传递数据替换插槽值;</MyDialog>
  </div>
</template>

<script>
import MyDialog from './components/MyDialog.vue'
export default {
  name: 'App',
  data(){ return { } },
  components:{ MyDialog }
}
</script> 
<style></style>

具名插槽:

具名插槽(Named SlotsVue.js中用于组件间内容分发的一种高级特性,它允许开发者在子组件中定义多个插槽,

父组件可以指定内容插入到子组件的特定插槽中,这种机制提高了组件复用性灵活性,特别是在构建复杂UI结构时

通常情况下,默认插槽:<组件> 替换插槽内容 </组件> 仅支持一个定义插槽,传值;

当子组件需要多个插槽 来接收不同部分的内容时,可以使用具名插槽:

具名插槽基本语法:

  • 在子组件的模板中,通过给<slot name="具名" >元素添加name属性来定义具名插槽;
  • 父组件在使用子组件时,通过v-slot指令指定内容应该插入到子组件的哪个具名插槽中,

Vue 3中,可以直接在v-slot后跟插槽名称,或者使用冒号前缀来指定;

./components/MyDialog.vue:

代码语言:html
复制
<template>
  <div class="dialog">
    <!-- 一旦插槽起了名字,就是具名插槽,只支持定向分发 -->
    <div class="dialog-header">
      <slot name="head">插值头</slot>         <!-- 定义组件插值头 -->     
    </div>
    <div class="dialog-content">
      <slot name="content">插值内容</slot>    <!-- 定义组件插值内容 -->     
    </div>
    <div class="dialog-footer">
      <slot name="footer">插值内容</slot>     <!-- 定义组件插值底部 -->     
    </div>
  </div>
</template>

App.vue: 主组件调用——>插槽组件并传值,运行查看效果;

代码语言:html
复制
<template>
  <div id="app">
    <!-- 调用具名插槽需要通过template标签包裹需要分发的结构,包成一个整体 -->
    <MyDialog>
      <!-- 通过`v-slot`指令指定内容应该插入到子组件的哪个具名插槽中 -->
      <template v-slot:head>我是大标题</template>
      <template v-slot:content>我是内容</template>
      <template v-slot:footer> <button>取消</button> <button>确认</button></template>
    </MyDialog>
  </div>
</template>

<script>
import MyDialog from './components/MyDialog.vue'
export default {
  name: 'App',
  data(){ return { } },
  components:{ MyDialog }
}
</script> 
<style></style>

作用域插槽:

作用域插槽Scoped SlotsVue.js提供的一种高级插槽机制:

普通插槽: 某种意义上类似于,父组件——传递特定数据——渲染子组件,算是一种:父——子通信

作用域插槽: 它允许子组件向父组件传递数据,使得父组件在使用子组件的插槽时能够访问到子组件的内部数据;

这一特性在Vue 2.6中引入,并在Vue 3中通过更简洁的v-slot语法得到进一步的优化和推广;

作用域插槽的核心在于,它创建了一个局部作用域: 这个作用域内的数据由子组件提供;

父组件可以通过插槽来访问这些数据,这使得父组件可以根据子组件的状态\数据:

动态地渲染内容,而无需直接访问子组件的内部状态;

作用域插槽语法:

  • 子组件:给slot 标签以添加属性的方式传值:所有添加的属性,都会被收集到一个对象中传递;
  • 父组件:template中通过#插槽名= "变量名" 接收确认匹配的插槽,并将数据赋值变量名方便使用,默认插槽名为 #defaul

Demo案例:

封装表格组件: 数据由父组件提供,传递子组件渲染表格,但:数据修改\删除\查询操作还在父组件;

子组件仅是单纯的渲染数据,并支持根据需求自定义:修改\删除\查询操作按钮权限;

而:父组件操作表格信息,就要获取对应信息的ID: 子组件—通过插槽形式—传递父组件

./components/MyDialog.vue:

代码语言:html
复制
<template>
  <table class="my-table">
    <thead>
      <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>年纪</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <!-- 列表数据还是原始的父子传递形式; -->
      <!-- 子组件循环渲染表格 -->
      <tr v-for="(item, index) in data" :key="item.id">
        <td>{{ index + 1 }}</td>
        <td>{{ item.name }}</td>
        <td>{{ item.age }}</td>
        <!-- 插槽自定义操作项: -->
        <td>
          <!-- 给slot标签添加属性的方式传值 -->
          <!-- 所有添加的属性,都会被收集到一个JSON对象中发送至父组件 -->
          <slot :row="item" msg="测试文本" ></slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>
<script>
  export default {
    props: { data: Array }  //接受父组件传递数据;
  }
</script>

App.vue: 主组件调用——>插槽组件并传值,运行查看效果;

代码语言:html
复制
<template>
  <div id="app">
    <!-- 引入表格组件: 并自定义其支持删除操作 -->
    <MyTable :data="list" >                             <!-- :data 父—传递—子数据 -->
      <template #default="obj">                         <!-- 父组件自定义组件按钮,并获取子组件传递数据信息; -->
        <button @click="del(obj.row.id)">删除</button>  <!-- 在template中,通过 #插槽名="自定义变量名" 接收,默认插槽名为 #default -->
      </template>
    </MyTable>             

    <!-- 引入表格组件: 并自定义其支持查询操作 -->
    <MyTable :data="list" >
      <template #default="obj">
        <button @click="del(obj.row.id)">删除</button>
        <button @click="show(obj.row)">查看</button>
        <button @click="showdata(obj)">数据</button>
      </template>
    </MyTable>
  </div>
</template>

<script>
import MyTable from './components/MyTable.vue'
export default {
  name: 'App',
  data(){
    return {    //定义表格组件渲染数据: 
      list: [
        { id: 1, name: '张小花', age: 18 },
        { id: 2, name: '孙大明', age: 19 },
        { id: 3, name: '刘德忠', age: 17 },
      ]
    } 
  },
  methods: {  //父组件对应操作函数;
    del (id) { this.list = this.list.filter(item => item.id !== id) },
    show (row) { alert(`姓名:${row.name}; 年纪:${row.age}`) },
    showdata(row) { console.log(row) },                               //将子组件solt中定义的值封装成一个JSON对象全传递过来;
  },
  components:{ MyTable }
}
</script> 

代码管理:

本代码已经使用Git进行管理: 公众号回复:Vue项目工程化

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Vue自定义指定、插槽🛠️:
  • 自定义指令:
    • 全局自定义指令:
      • 局部自定义指令:
        • 自定义指令—传值:
        • 插槽:
          • 插槽Slot:
            • 定义插槽:
              • 具名插槽:
                • 作用域插槽:
                  • 作用域插槽语法:
                  • Demo案例:
              • 代码管理:
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档