Appearance
vue3、jsx的插槽
本节目标:理解“vue3、jsx的插槽”的核心思路,并能把它用于实际开发或面试表达。
说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 一、
setup的两个参数,插槽在vue3的体现
{/* jsx的写法
*/}import{defineComponent}from'vue';exportdefaultdefineComponent({name:'',setup(props,context){// Attribute (非响应式对象,等同于
$attrs)console.log(context.attrs)====// 插槽 (非响应式对象,等同于
$slots)console.log(context.slots)====// 触发事件 (方法,等同于
$emit)console.log(context.emit)====// 暴露公共 property (函数
)console.log(context.expose)return{};},});
props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。 context:上下文对象 (也可以解构成
{attrs, slots, emit, expose})
attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs。 slots: 收到的插槽内容, 相当于 this.$slots。 emit: 分发自定义事件的函数, 相当于 this.$emit。 expose: 暴露公共 property (函数)。 注意:attrs 和 slots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用 property。请注意,与 props 不同,attrs 和 slots 的 property 是非响应式的。如果你打算根据 attrs 或 slots 的更改应用副作用,那么应该在 onBeforeUpdate 生命周期钩子中执行此操作。 二、插槽分类 1、匿名插槽(默认插槽:就是没有给名字的,默认为:default); 2、具名插槽(默认插槽:有name的,在一些UI框架中已经定义好的,只能调用定义好的插槽名,才会生效); 3、自定义插槽;(根据自己的需求进行命名) 三、话不多说,先上实现的效果图
布局容器-实现效果图 四、vue3、jsx的实现方式 1、代码实现:插槽 slots ,构建容器布局
{/* jsx的写法
*/}setup(props,context){return()=>({/*利用插槽 构建容器布局 核心代码块
*/}<><divclass="layout"><divclass={{'content':true,}}>{/*上下布局,头部部分
*/}{context.slots['top-section']?(<divclass="top-section">{/* <slot name="top-section"></slot> */}{context.slots['top-section']?.()}</div>):null}====
{/*上下布局,主容器部分
*/}<divclass={{'container-section-flex':context.slots['aside-section'],'container-section':true,}}>{/*主容器 左右布局,左边部分
*/}{context.slots['aside-section']?(<divclass="aside-section">{/* <slot name="aside-section"></slot> */}{context.slots['aside-section']?.()}</div>):null}====
{/*主容器 左右布局,右边部分
*/}<divclass="main-section"v-loading={props.loading}element-loading-text="==拼命加载中==
...">{/*主容器 头部部分
*/}{context.slots['head-section']?(<divclass="head-section">{/* <slot name="head-section"></slot> */}{context.slots['head-section']?.()}</div>):null}====
{/*主容器 选项部分
*/}{context.slots['opts-section']?(<divclass="opts-section">{/* <slot name="opts-section"></slot> */}{context.slots['opts-section']?.()}</div>):null}====
{/* <!--主容器 数据内容部分
--> */}<divclass="data-section"style="overflow: hidden;">{/* <slot name="data-section"></slot> */}{context.slots['data-section']?.()}</div>{/*主容器 分页部分
*/}<divclass="paging-section">{/* <slot name="paging-section"></slot> */}{context.slots['paging-section']?.()}</div></div></div></div>{/*默认插槽:
<slot></slot> */}{context.slots['default']?.()}</div></>);},样式自己需要写!!!! 2、插槽调用(v-slots={ })
{/* jsx的写法 核心调用代码块
*/}constslots={{/*默认插槽
*/}default:()=>{return(<><span>{'==默认区域==
'}</span></>);},{/*自定义插槽
*/}'top-section':()=>{return(<><span>{'==头部导航栏==
'}</span></>);},'aside-section':()=>{return(<><span>{'==侧边导航栏==
'}</span></>);},'main-section':()=>{return(<><span>{'==主体内容区==
'}</span></>);},'head-section':()=>{return(<><span>{'==头部内容区==
'}</span></>);},'opts-section':()=>{return(<><span>{'==中间内容区==
'}</span></>);},'data-section':()=>{return(<><span>{'==数据展示区==
'}</span></>);},'paging-section':()=>{return(<><span>{'==底部区==
'}</span></>);},};====
return()=>({/*插槽布局组件调用
*/}<content-layoutv-slots={slots}/>)五、对比vue2的template的模板写法 1、template模板代码实现方式
<slot ></slot>
<template><divclass="container-layout"><divclass="container"><!--上下布局,头部部分
--><divclass="top-section"v-if="$slots['top-section']"><slotname="top-section"></slot></div><!--上下布局,主容器部分
--><divclass="container-section":class="{'container-section-flex': $slots['aside-section']}"><!--主容器 左右布局,左边部分
--><divclass="aside-section"v-if="$slots['aside-section']"><slotname="aside-section"></slot></div><!--主容器 左右布局,右边部分
--><divclass="main-section"v-loading="loading"element-loading-text="==拼命加载中==
..."><!--主容器 头部部分
--><divclass="head-section"v-if="$slots['head-section']"><slotname="head-section"></slot></div><!--主容器 选项部分
--><divclass="opts-section"v-if="$slots['opts-section']"><slotname="opts-section"></slot></div><!--主容器 数据内容部分
--><div><slotname="data-section"></slot></div><div><slotname="paging-section"></slot></div></div></div></div><slot></slot></div></template>====``====
2、插槽调用( \<template v-slot:name /\> 或者 \<template #name /\>) {/* ==部分调用==
*/}<div><templatev-slot:top-section>==顶部导航栏==
</template><template#aside-section>==侧边菜单栏==
</template><template#data-section>==数据内容部分==
</template><template#default>==默认数据==
</template></div>==== ------------------ 2022.04.22 追更 解决
wilikeit提出的 slot-scope的问题 ---------------------- 六、回调参数:以el-upload为例:
slot-scope="{file}"
1、vue2的模板写法 \<!-- vue2 template写法
--><el-upload><divslot="file"slot-scope="{file}"@click="handlePictureCardPreview(file)"><imgclass="el-upload-list__item-thumbnail":src="file.url"alt=""/></div></el-upload>==== 2、vue3、tsx的模板写法
{/* tsx写法
*/}constuploadSlot={file:(file:any)=>{console.log(file);//打印结果,图6-3如示
return(<><div><imgclass="el-upload-list__item-thumbnail"src={file.url}alt=""/></div></>);})}====
<el-uploadv-slots={uploadSlot}/>
3、file回调值,效果图
图6-3 file回调值,效果图
--------------------- 2022.04.27 追更 解决
wilikeit提出的 @node-click的问题 ---------------------- 七、解决
wilikeit提出的\<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" /\>中@node-click这种怎么写在jsx中的问题 特别提示: onClick={ 这里写的是函数,而不是变量变量,有的同学一直在这里写变量,导致函数一直无法响应
}
constfun=()=>{}//错误写法
onClick={fun}//正确写法
,onClick={()=>{fun}}interfaceTree{label:string;children?:Tree[];}consthandleNodeClick=(data:Tree,node:any,obj:any)=>{console.log(data,node,obj);};constdata:Tree[]=[{label:'Level one 1',children:[{label:'Level two 1-1',children:[{label:'Level three 1-1-1',},],},],},]constdefaultProps={children:'children',label:'label',};<!--vuetemplate==写法==
--><el-tree:data="data":props="defaultProps"@node-click="handleNodeClick"/>{/* tsx的写法
*/}<el-treedata={data}props={defaultProps}onNodeClick={(e:any,node:any,obj:any)=>{handleNodeClick(e,node,obj);}}/>{/*点击事件效果,如下图7-1所示
*/}@wilikeit将@node-click转化成onNodeClick(或者onNode-click 也可以,但不规范,有时也不生效) 传参问题:事件node-click有三个回调参数,三个参数:对应于节点点击的节点对象, TreeNode ==节点== 属性,事件对象 (e: any, node: any, obj: any)
图7-1、node点击事件打印结果-实现效果图 有不理解的,请在评论区告诉我!有求必应! 最后,原创不易,点个赞再走呗! 编辑于 2022-04-27 10:40 > 来自
<https://zhuanlan.zhihu.com/p/501806945>