Skip to main content

组件封装

插槽

第一版插槽案例:

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// function Hello({msg='',className='row'}){
function Hello(params){
let header='',footer='';
const {msg,className='row',children=null} = params;
if(children!==null){
if(Array.isArray(children)){
children.forEach(item=>{
console.log(item.props,Object.prototype.toString.call(item))
if(Object.hasOwn(item.props,'slot')){
switch(item.props.slot.toLowerCase()){
case 'header':
header=item;
break;
case 'footer':
footer = item;
break;
}
}
});
}else if(Object.hasOwn(children.props,'slot')){
switch(children.props.slot.toLowerCase()){
case 'header':
header=children;
break;
case 'footer':
footer = children;
break;
}
}
}
return (
<div className={`hello ${className}`}>
<div>{header}</div>
{msg && '条件渲染--有传参msg'}~{msg}
<div>{footer}</div>
</div>
)
}
export default Hello;

第二版插槽案例:

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function ComponentWithSlots(Component){
return ({children,...props})=>{
const slots = {};
const defaultSlots = [];
if(children){
React.Children.forEach(children,child=>{
console.log(child.type.name)
if(React.isValidElement(child) && child.type.name=='Slot'){
const { name, children: slotContent } = child.props;
if (name) {
slots[name] = slotContent;
} else {
defaultSlots.push(slotContent);
}
} else {
defaultSlots.push(child);
}
})
console.log('utils',slots)
}
return (
<Component {...props} slots={slots} defaultSlot={defaultSlots}/>
);
}
}
function Slot({children}){
return children;
}
const Hello = ComponentWithSlots(({msg, className, slots, defaultSlot})=>{
return (
<div className={`hello ${className}`}>
{slots?.header && (
<div className="header-slot">{slots.header}</div>
)}
{msg && '条件渲染--有传参msg'}~{msg}
{slots?.footer && (
<div className="footer-slot">{slots.footer}</div>
)}
</div>
);
});

为什么不直接使用children,而是使用React.children?

通过官方文档得知,react.children被标记为过时的api.

第三版插槽

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import {createContext,useContext,useRef,useMemo} from "react";
//创建slot上下文
const SlotContent = createContext({});
//插槽容器组件
function SlotProvider({children,component:Component,...props}){
const slotsRef = useRef({})
return (
<SlotContent.Provider value={slotsRef.current}>
{children}
<Component {...props} slots={slotsRef.current}/>
</SlotContent.Provider>
);
}
//slot组件
function Slot({name,children}){
const slots = useContext(SlotContent);
//注册插槽内容
useMemo(()=>{
if(name){
// console.log('slot',children)
slots[name] = children;
}else{
slots.default = children
}
},[name,children,slots]);
return null;
}
export {Slot,SlotProvider}
import {SlotProvider} from '@/components/utils'
//组件v2版
const Hello = (({msg,slots})=>{
console.log(slots.current)
return (
<div className='hello'>
{slots.header && (
<div className="header-slot">{slots.header}</div>
)}
{slots.default ? <div>{slots.default}</div> : <div>{msg}</div>}
{slots.footer && (
<div className="footer-slot">{slots.footer}</div>
)}
</div>
);
});
function A(params){
console.log(params)
return (
<>
<div>one-a</div>
<SlotProvider component={Hello} msg="hello world"/>
<SlotProvider component={Hello} msg='ok~ok'>
<Slot name='header'>
<div>header1-row1</div>
<div>header1-row2</div>
</Slot>
</SlotProvider>
<SlotProvider component={Hello}>
<Slot name='header'>
<div>header2-row1</div>
<div>header2-row2</div>
</Slot>
<Slot name='footer'>
<div>footer2-row1</div>
<div>footer2-row2</div>
<div>footer2-row3</div>
</Slot>
</SlotProvider>
</>
);
}