一、组件化
1、使用组合思想来实现组件化
在react中组件的复用,是组合还是继承?是组合
2、一个正常的组件有哪些东西
3、字段校验
(1)一般校验两种:必填非必填,是什么类型
(2)PropTypes.element(react元素类型):包含jsx、string、null、undifined
(3)必填属性使用isRequired来约束,非必填属性一般给默认值
(4)所有属性都要给类型约束
(5)PropTypes.oneOf(['primary', 'danger', 'info']):取值是其中之一
(6)oneOfType([PropTypes.bool,PropTypes.instanceOf(Array), PropTypes.element]):取值类型是其中之一
(7)PropTypes.node:比PropTypes.element范围更广
(8)指定user这个对象类型的结构
user: PropTypes.shape({
name: PropTypes.string.isRequired,
age: PropTypes.number,
})
4、函数命名规则
(1)如果一个函数参与视图渲染,前面不加on
(2)如果一个函数跟事件有关,前面加on
5、什么是render props
(1)在封装React组件时,如果一个props属性其值是函数类型并返回JSX视图结果,那么这个props属性就是render props,即,可以参与组件的视图渲染。例如:<Modal footer={()=>{xxx}}。这个footer属性就被称为render props
(2)render props在组件化中,用的非常多
6、{...props}
属性穿透
父组件给的props,直接给到子组件
7、Protal
(1)protal提供了一种将子节点渲染到存在于父组件以外的DOM节点的方案,比如我要将弹窗挂载到body上
(2)可以把一段JSX结构,“穿梭”插入到父级节点中的一种能力
语法:ReactDOM.createPortal(<jsx/>,dom节点)
8、到底什么是组合,怎么理解组合
组合是一种设计模式,它是React组件化的基础
9、如何封装组件
第一步,根据UI视图拆解结构,得到一个一个可独立封装的小组件
第二步,把这些独立的小部件分别封装
第三步,再使用props把这些小组件串联起来
二、例子
import '@/assets/modal.scss'; import PropTypes from 'prop-types'; import { useState } from 'react'; import { createPortal } from 'react-dom'; // 封装button组件 function Button (props) { const { type, children, onClick } = props; return ( <div className='self-button'> <span className={type} onClick={onClick}>{children}</span> </div> ); } Button.propTypes = { type: PropTypes.oneOf(['default', 'primary', 'danger', 'info']), children: PropTypes.string, onClick: PropTypes.func, }; Button.defaultProps = { type: 'default', children: '点击按钮', }; // 封装组件 function Modal (props) { const { title, closable, children, type, visible, onCancel, onOk, onDelete, footer } = props; const randerFooter = () => { switch (type) { case 'confirm': return ( <div> <Button type="primary" onClick={onOk}>确认</Button> <Button onClick={onCancel}>取消</Button> </div> ); case 'danger': return ( <div> <Button type="danger" onClick={onDelete}>删除</Button> <Button onClick={onCancel}>取消</Button> </div> ); case 'info': return ( <div> <Button type="info" onClick={onOk}>确认</Button> <Button onClick={onCancel}>取消</Button> </div> ); default: return ( <div> <Button type='info' onClick={onCancel}>我知道了</Button> </div> ); } }; const handlerLayer = (ev) => { console.log('---ev.currentTarget', ev.currentTarget.dataset); console.log('---ev.target', ev.target.dataset); if (ev.target.dataset.self === 'layer') { // 防止事件冒泡到父元素 onCancel(); } }; return ( <div className="layer" style={{ display: visible ? 'block' : 'none' }} >.layer { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(0, 0, 0, 0.7); .modal { width: 520px; background: white; border-radius: 3px; position: absolute; top: 150px; left: 50%; margin-left: -260px; box-sizing: border-box; header { line-height: 50px; padding: 0 20px; border-bottom: 1px solid #eee; font-size: 12px; &>div { width: 100%; height: 100%; overflow: hidden; &>div:first-child { float: left; } &>div:last-child { float: right; cursor: pointer; } } } footer { line-height: 50px; padding: 0 20px; border-top: 1px solid #eee; height: 50px; overflow: hidden; &>div>div { // 嵌套写法 &.self-button { float: right; } } } main { box-sizing: border-box; padding: 20px; font-size: 14px; } } } .self-button { display: inline-block; // 行内块元素 height: 30px; box-sizing: border-box; line-height: 25px; font-size: 12px; border-radius: 2px; border: 1px solid transparent; overflow: hidden; margin: 0 10px; cursor: pointer; span { display: block; padding: 0 15px; box-sizing: border-box; width: 100%; height: 100%; } span.default { color: #333; border: 1px solid #333; } span.primary { color: #fff; background-color: #007bff; border: 1px solid #007bff; } span.danger { color: #fff; background-color: #dc3545; border: 1px solid #dc3545; } span.info { color: #fff; background-color: #28a745; border: 1px solid #28a745; } }