ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

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

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

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

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

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

学习前置链接:懂个锤子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绑定到指令的值、expression、arg、modifiers等属性;

  • vnode 和 oldVnode:Vue编译后的虚拟节点,用于更复杂的操作;

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

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

//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引入并调用:

importDirectivesfrom'./JS/directives'//自定义全局指令, JS文件目录;importVuefrom'vue'Vue.use(Directives);

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

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

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

局部自定义指令:

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

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

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

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

自定义指令—传值:

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

<template><divid="app"><inputtype="text"v-model="color"><h1v-color="color">局部自定义指令</h1><!-- 设置v-color值来自: 输入框,同步 --></div></template><script>exportdefault{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>占位,父组件定制传入;

<template><divclass="dialog"><divclass="dialog-header"><h3>警告:</h3><spanclass="close">✖️</span></div><divclass="dialog-content"><!-- 在需要定制的位置,使用slot占位 --><slot>插值:默认显示内容;</slot></div><divclass="dialog-footer"><button>取消</button><button>确认</button></div></div></template><script>exportdefault{data(){return{}}}</script><!-- 省略样式代码 --><stylescoped></style>

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

<template><divid="app"><!-- 外部使用组件时,不传东西,则slot会显示后备内容 --><MyDialog></MyDialog><!-- 外部使用组件时,传东西了,则slot整体会被换掉 --><MyDialog>组件调用完成,传递数据替换插槽值;</MyDialog></div></template><script>importMyDialogfrom'./components/MyDialog.vue'exportdefault{name:'App',data(){return{}},components:{MyDialog}}</script><style></style>

具名插槽:

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

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

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

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

具名插槽基本语法:

  • 在子组件的模板中,通过给<slot name="具名" >元素添加name属性来定义具名插槽;

  • 父组件在使用子组件时,通过v-slot指令指定内容应该插入到子组件的哪个具名插槽中,

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

./components/MyDialog.vue:

<template><divclass="dialog"><!-- 一旦插槽起了名字,就是具名插槽,只支持定向分发 --><divclass="dialog-header"><slotname="head">插值头</slot><!-- 定义组件插值头 --></div><divclass="dialog-content"><slotname="content">插值内容</slot><!-- 定义组件插值内容 --></div><divclass="dialog-footer"><slotname="footer">插值内容</slot><!-- 定义组件插值底部 --></div></div></template>

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

<template><divid="app"><!-- 调用具名插槽需要通过template标签包裹需要分发的结构,包成一个整体 --><MyDialog><!-- 通过`v-slot`指令指定内容应该插入到子组件的哪个具名插槽中 --><templatev-slot:head>我是大标题</template><templatev-slot:content>我是内容</template><templatev-slot:footer><button>取消</button><button>确认</button></template></MyDialog></div></template><script>importMyDialogfrom'./components/MyDialog.vue'exportdefault{name:'App',data(){return{}},components:{MyDialog}}</script><style></style>

作用域插槽:

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

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

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

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

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

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

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

作用域插槽语法:

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

Demo案例:

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

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

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

./components/MyDialog.vue:

<template><tableclass="my-table"><thead><tr><th>序号</th><th>姓名</th><th>年纪</th><th>操作</th></tr></thead><tbody><!-- 列表数据还是原始的父子传递形式; --><!-- 子组件循环渲染表格 --><trv-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>exportdefault{props:{data:Array}//接受父组件传递数据;}</script>

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

<template><divid="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>importMyTablefrom'./components/MyTable.vue'exportdefault{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进行管理:

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进