组件封装
插槽
第一版插槽案例:
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></>);}