网站首页 > 基础教程 正文
原文的篇幅非常长,不过内容太过于吸引我,还是忍不住要翻译出来。此篇文章对编写可重用和可维护的React组件非常有帮助。但因为篇幅实在太长,我对文章进行了分割,本篇文章重点阐述 封装。因本人水平有限,文中部分翻译可能不够准确,如果您有更好的想法,欢迎在评论区指出。
封装
一个封装组件提供 props 控制其行为而不是暴露其内部结构。
耦合是决定组件之间依赖程度的系统特性。根据组件的依赖程度,可区分两种耦合类型:
- 当应用程序组件对其他组件知之甚少或一无所知时,就会发生松耦合。
- 当应用程序组件知道彼此的许多详细信息时,就会发生紧耦合。
松耦合是我们设计应用结构和组件之间关系的目标。
松耦合应用(封装组件)
松耦合会带来以下好处:
- 可以在不影响应用其它部分的情况下对某一块进行修改。、
- 任何组件都可以替换为另一种实现
- 在整个应用程序中实现组件复用,从而避免重复代码
- 独立组件更容易测试,增加了测试覆盖率
相反,紧耦合的系统会失去上面描述的好处。主要缺点是很难修改高度依赖于其他组件的组件。即使是一处修改,也可能导致一系列的依赖组件需要修改。
紧耦合应用(组件无封装)
封装 或 信息隐藏 是如何设计组件的基本原则,也是松耦合的关键。
我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年年初我花了一个月整理了一份最适合2019年学习的web前端学习干货,各种框架都有整理,送给每一位前端小伙伴,想要获取的可以关注我的头条号并在后台私信我:前端,即可免费获取。
信息隐藏
封装良好的组件隐藏其内部结构,并提供一组属性来控制其行为。
隐藏内部结构是必要的。其他组件没必要知道或也不依赖组件的内部结构或实现细节。
React 组件可能是函数组件或类组件、定义实例方法、设置 ref、拥有 state 或使用生命周期方法。这些实现细节被封装在组件内部,其他组件不应该知道这些细节。
隐藏内部结构的组件彼此之间的依赖性较小,而降低依赖度会带来松耦合的好处。
通信
细节隐藏是隔离组件的关键。此时,你需要一种组件通信的方法:props。porps 是组件的输入。
建议 prop 的类型为基本数据(例如,string 、 number 、boolean):
<Message text="Hello world!" modal={false} />;
必要时,使用复杂的数据结构,如对象或数组:
<MoviesList items={['Batman Begins', 'Blade Runner']} />
prop 可以是一个事件处理函数和异步函数:
<input type="text" onChange={handleChange} />
prop 甚至可以是一个组件构造函数。组件可以处理其他组件的实例化:
function If({ component: Component, condition }) { return condition ? <Component /> : null; } <If condition={false} component={LazyComponent} />
为了避免破坏封装,请注意通过 props 传递的内容。给子组件设置 props 的父组件不应该暴露其内部结构的任何细节。例如,使用 props 传输整个组件实例或 refs 都是一个不好的做法。
访问全局变量同样也会对封装产生负面影响。
案例研究:封装修复
组件的实例和状态对象是封装在组件内部的实现细节。因此,将状态管理的父组件实例传递给子组件会破坏封装。
我们来研究一下这种情况。
一个简单的应用程序显示一个数字和两个按钮。第一个按钮增加数值,第二个按钮减少数值:
核心代码:
class App extends React.Component { constructor(props) { super(props); this.state = { number: 0 }; } render() { return ( <div className="app"> <span className="number">{this.state.number}</span> <Controls parent={this} /> </div> ); } } class Controls extends React.Component { render() { return ( <div className="controls"> <button onClick={() => this.updateNumber(+1)}> Increase </button> <button onClick={() => this.updateNumber(-1)}> Decrease </button> </div> ); } updateNumber(toAdd) { this.props.parent.setState(prevState => ({ number: prevState.number + toAdd })); } } ReactDOM.render(<App />, document.getElementById('root'));
<Controls> 负责渲染按钮,并为其设置事件处理函数,当用户点击按钮时,父组件的状态将会被更新:number 加1或者减1 (updateNumber()方法)
当前的实现有什么问题?
- 第一个问题是:<App> 的封装被破坏,因为它的内部结构在应用中传递。<App> 错误地允许 <Controls> 直接去修改其 state。
- 第二个问题是: 子组件 Controls 知道了太多父组件 的内部细节,它可以访问父组件的实例,知道父组件是一个有状态组件,知道父组件的 state 对象的细节(知道 number 是父组件 state 的属性),并且知道怎么去更新父组件的 state.
// 问题: 使用父组件的内部结构 class Controls extends Component { render() { return ( <div className="controls"> <button onClick={() => this.updateNumber(+1)}> Increase </button> <button onClick={() => this.updateNumber(-1)}> Decrease </button> </div> ); } updateNumber(toAdd) { this.props.parent.setState(prevState => ({ number: prevState.number + toAdd })); } }
这样就会导致:<Controls> 将很难测试和重用。对 <App> 结构的细微修改会导致需要对 <Controls> 进行修改(对于更大的应用程序,也会导致类似耦合的组件需要修改)。
解决方案是设计一个方便的通信接口,考虑到松耦合和封装。让我们改进两个组件的结构和属性,以便恢复封装。
只有组件本身应该知道它的状态结构。<App> 的状态管理应该从 <Controls>(updateNumber()方法)移到正确的位置:即 <App> 组件中。
<App> 被修改为 <Controls> 设置属性 onIncrease 和 onDecrease。这些是更新 <App> 状态的回调函数:
// 解决: 恢复封装 class App extends Component { constructor(props) { super(props); this.state = { number: 0 }; } render() { return ( <div className="app"> <span className="number">{this.state.number}</span> <Controls onIncrease={() => this.updateNumber(+1)} onDecrease={() => this.updateNumber(-1)} /> </div> ); } updateNumber(toAdd) { this.setState(prevState => ({ number: prevState.number + toAdd })); } }
现在,<Controls> 接收用于增加和减少数值的回调,注意解耦和封装恢复时:<Controls> 不再需要访问父组件实例。也不会直接去修改父组件的状态。
而且,<Controls> 被修改为了一个函数式组件:
// 解决方案: 使用回调函数去更新父组件的状态 function Controls({ onIncrease, onDecrease }) { return ( <div className="controls"> <button onClick={onIncrease}>Increase</button> <button onClick={onDecrease}>Decrease</button> </div> ); }
<App> 组件的封装已经恢复,状态由其本身管理,也应该如此。
此外,<Controls> 不在依赖 <App> 的实现细节,onIncrease 和 onDecrease 在按钮被点击的时候调用,<Controls> 不知道(也不应该知道)这些回调的内部实现。
<Controls> 组件的可重用性和可测试性显著增加。
<Controls> 的复用变得很容易,因为它除了需要回调,没有其它依赖。测试也变得简单,只需验证单击按钮时,回调是否执行。
最后谢谢各位小伙伴愿意花费宝贵的时间阅读本文,如果本文给了您一点帮助或者是启发,请不要吝啬你的赞和Star,您的肯定是我前进的最大动力。
翻译:刘小夕原文链接:https://dmitripavlutin.com/7-architectural-attributes-of-a-reliable-react-component/
- 上一篇: React.js前端框架初学技术总结
- 下一篇: React开发必须知道的34个技巧
猜你喜欢
- 2024-11-24 React源码分析与实现(一):组件的初始化与渲染「实践篇」
- 2024-11-24 React 最简单的入门应用项目
- 2024-11-24 「干货」深入浅出React组件逻辑复用的那些事儿
- 2024-11-24 「干货满满」React Hooks 最佳实践
- 2024-11-24 React开发必须知道的34个技巧
- 2024-11-24 React.js前端框架初学技术总结
- 2024-11-24 前端架构师成长之路:5 分分钟搞懂面试官必问 React 题
- 2024-11-24 只会Vue的我,用两天学会了react,这个方法您也可以
- 2024-11-24 react高质量笔记_9(Diffing算法)
- 2024-11-24 说说你在使用React 过程中遇到的常见问题?
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)