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进行管理: