4. React组件插槽用法
4.1. React中的插槽(Slot
)
-
- 在开发中,我们抽取一个组件,但是为了让这个组件具备更强的通用性,我们不能将组件中的内容限制为固定的div、span等等这些元素
-
-
- 这种需求在Vue当中有一个固定的做法是通过slot来完成的,React呢?
- 在React中是没有插槽的概念的,或者说React中不需要插槽,因为React太灵活了,React中可以通过多种形式来实现插槽的效果,例如可以传递任意属性给子组件
-
- React对于这种需要插槽的情况非常灵活,有两种方案可以实现:
- 组件的
children子元素
; props属性传递
React元素`;
4.2. children实现插槽
-
- 每个组件之间都可以获取到
props.children属性
:它包含组件的开始标签和结束标签之间的内容
。
- 每个组件之间都可以获取到
-
- 示例代码如下:
-
如图:
-
父组件代码:
js// React中是没有插槽的概念的,或者说React中不需要插槽,因为React太灵活了,React中可以通过多种形式来实现插槽的效果,例如可以传递任意属性给子组件 import React, { Component } from 'react' import NavBar from './nav-bar' export class App extends Component { render() { // NavBar实例对象 -> this // this.props.children -> [button, h2, i] return ( <div> <NavBar> <button>按钮</button> <h2>我是标题</h2> <i>我是斜体字</i> </NavBar> </div> ) } } // function createElement(type, props, children) { // children -> arguments.length - 2 // } export default App
- 子组件代码:
jsimport React, { Component } from 'react' import './style.css' import PropTypes from 'prop-types' export class NavBar extends Component { render() { const { children } = this.props // 子元素放多个的时候,children是一个数组,一个的时候是一个对象 console.log('children===', children) return ( <div className='nav-bar'> <div>{children}</div> {/* <div className="left">{children[0]}</div> <div className="center">{children[1]}</div> <div className="right">{children[2]}</div> */} </div> ) } } NavBar.propTypes = { children: PropTypes.array } export default NavBar
4.3. props实现插槽
-
- 通过
children
实现的方案虽然可行,但是有以下两个弊端:
children的弊端
:childre
n可能是一个元素
也可能是数组
,使时需要慎重
children
的对索引顺序要求太高
,通过索引值获取传入的元素很容易出错
,不能准确的获取传入的元素
;
- 通过
-
- 另外一种方案就是
使用props实现
:
通过具体的属性名,可以让我们在传入和获取时更加的精准
- 另外一种方案就是
-
- 实例代码如下:
- 父组件代码如下:
js// React中是没有插槽的概念的,或者说React中不需要插槽,因为React太灵活了,React中可以通过多种形式来实现插槽的效果,例如可以传递任意属性给子组件 import React, { Component } from 'react' import NavBar from './nav-bar' import NavBarTwo from './nav-bar-two' export class App extends Component { render() { // NavBar实例对象 -> this // this.props.children -> [button, h2, i] const btn = <button>按钮</button> return ( <div> {/* 1.吃用children实现插槽 */} <NavBar> <button>按钮</button> <h2>我是标题</h2> <i>我是斜体字</i> </NavBar> {/* 2.使用props现插槽实 */} {/* 推荐使用props方案,children有点不可控 */} <NavBarTwo style={{marginTop: '20px'}} leftSlot={ btn } centerSlot={ <h2>呵呵呵呵</h2> } rightSlot={ <i>斜体2</i> } /> </div> ) } } // function createElement(type, props, children) { // children -> arguments.length - 2 // } export default App
- 子组件代码如下:
jsimport React, { Component } from 'react' export class NavBarTwo extends Component { render() { const { leftSlot, centerSlot, rightSlot } = this.props return ( <div className='nav-bar'> <div className="left">{leftSlot}</div> <div className="center">{centerSlot}</div> <div className="right">{rightSlot}</div> </div> ) } } export default NavBarTwo
4.4. 作用域插槽实现
-
- 作用域插槽案例;现在子组件的item都是span,
希望子组件的内容是根据父组件传进来的类型渲染
- 作用域插槽案例;现在子组件的item都是span,
-
父组件代码:
jsimport React, { Component } from 'react' import TabControl from './TabControl' export class App extends Component { constructor() { super() this.state = { titles: ['流行', '新款', '热门'], tabIndex: 0 } } tabClick (index) { this.setState({ tabIndex: index }) } getTabItem (item) { if(item ==='流行') { return <span>{item}</span> } else if(item === '新款') { return <button>{item}</button> } else { return <i>{item}</i> } } render() { const { titles,tabIndex } = this.state // 现在子组件的item都是span,希望子组件的内容是根据父组件传进来的类型渲染 // 作用域插槽 return ( <div className='app'> <TabControl titles={ titles } tabClick={i => this.tabClick(i)} // itemType={(item) => <button>{item}</button>}/> itemType={(item) => this.getTabItem(item)}/> <h2>{titles[tabIndex]}</h2> </div> ) } } export default App
-
子组件代码:
jsimport React, { Component } from 'react' import './style.css' export class TabControl extends Component { constructor() { super() this.state = { currentIndex: 0 } } changeTab (index) { this.props.tabClick(index) this.setState({ currentIndex: index }) } render() { const { titles, itemType } = this.props const { currentIndex } = this.state return ( <div> <div className="tab-control"> { titles.map((item, index) => { return ( <div className={`item ${currentIndex === index ? 'active' : ''}`} key={index} onClick={(e) => this.changeTab(index)}> {/* <span className='text' >{item}</span> */} {itemType(item)} </div> ) }) } </div> </div> ) } } export default TabControl