组件化思想的应用:
把一个解决能力范围外的大问题拆解成一个个能力范围内的小问题以及可以理解的小问题之间的关系
个人一直理解的是拆成小问题而忽略了问题之间的关系,我觉得这句话对我思想上的帮助很大
在开发过程中,尽可能的将页面拆分成一个个小的、可复用的组件
让代码更加方便组织和管理,并且扩展性也更强。
React的组件相对于Vue更加的灵活和多样,按照不同的方式可以分成很多类组件:
根绝定义方式,可以分为:
函数组件(Functional Component )
类组件(Class Component)
根据组件内部是否有状态需要维护,可以分成:
无状态组件(Stateless Component )
有状态组件(Stateful Component);
根据组件的不同职责,可以分成:
展示型组件(Presentational Component)
容器型组件(Container Component);
这些概念有很多重叠,但是他们最主要是关注数据逻辑和UI展示的分离
函数组件、无状态组件、展示型组件主要关注UI的展示
类组件、有状态组件、容器型组件主要关注数据逻辑
组件的名称是大写字符开头(无论类组件还是函数组件)
类组件需要继承自 React.Component
类组件必须实现render函数
在ES6之前,可以通过create-react-class 模块来定义类组件,但是目前官网建议我们使用ES6的class类定义。
使用class定义一个组件:
这里面state内容声明是错误的!这儿只是代表能有多个变量被管理的意思
constructor是可选的,通常在constructor中初始化一些数据
this.state中维护的就是我们组件内部的数据
render() 方法是 class 组件中唯一必须实现的方法
当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:
React 元素:
通常通过 JSX 创建。
例如div会被 React 渲染为 DOM 节点,MyComponent会被 React 渲染为自定义组件;
无论是div还是MyComponent均为 React 元素。
Portals:可以渲染子节点到不同的 DOM 子树中。
字符串或数值类型:它们在 DOM 中会被渲染为文本节点
函数组件是使用function来进行定义的函数,只是这个函数会返回和类组件中render函数返回一样的内容
函数组件有自己的特点(暂时抛开hooks不谈)
React组件从创建到销毁的整个过程,这个过程称之为是React的生命周期
了解组件的生命周期可以让我们在最合适的地方完成自己想要的功能
生命周期的多个阶段:
装载阶段(Mount),组件第一次在DOM树中被渲染的过程;
更新过程(Update),组件状态发生变化,重新更新渲染的过程;
卸载过程(Unmount),组件从DOM树中被移除的过程;
React内部执行到一定阶段时候,组件内部实现的某些函数进行回调,这些函数就是生命周期函数:
我们可以在这些回调函数中编写自己的逻辑代码,来完成自己的需求功能
比如实现componentDidMount函数:组件已经挂载到DOM上时,就会回调
比如实现componentDidUpdate函数:组件已经发生了更新时,就会回调
比如实现componentWillUnmount函数:组件即将被移除时,就会回调
谈React生命周期时,主要谈的类的生命周期,因为函数式组件是没有生命周期函数的(暂时抛开hooks不谈)
这里提到了一个概念:方法和函数的区别——看有没有this,即有没有一个实例联系在一起,有实例联系的叫方法,没有的叫函数
通过代码验证生命周期函数的执行状态:
index.js
import React from "react";
import ReactDOM from "react-dom"
import App from "./App";
ReactDOM.render(<App/>,document.getElementById('root'));
App.js
import React,{
Component } from "react";
export class ChildCpn extends Component{
render(){
return(
<div>这是子组件ChildCpn</div>
)
}
componentWillUnmount(){
console.log("调用了ChildCpn的componentWillUnmount方法");
}
}
export default class App extends Component{
constructor(props){
super(props);
this.state = {
counter : 0,
isShow : true,
}
console.log("执行了constructor方法");
}
render(){
console.log("执行了render方法");
return (
<div>
<div>这是组件App</div>
<div>{
this.state.counter}</div>
<button onClick={
e => {
this.btnclick()}}> increase </button>
<hr></hr>
<button onClick={
e => {
this.isShow()}}>IsShow</button>
{
this.state.isShow && <ChildCpn/>}
</div>
)
}
componentDidMount(){
console.log("执行了componentDidMount方法");
}
componentDidUpdate(){
console.log("执行了componentDidUpdate方法");
}
btnclick(){
this.setState({
counter: this.state.counter + 1
})
}
isShow(){
this.setState({
isShow:!this.state.isShow
})
}
}
验证效果:
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
constructor中通常只做两件事情:
通过给 this.state 赋值对象来初始化内部的state
为事件绑定实例(this)
componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。
componentDidMount中通常进行操作:
依赖于DOM的操作
发送网络请求(官方建议)
可以在此处添加一些订阅(会在componentWillUnmount取消订阅,我也不懂什么是订阅)
componentDidUpdate() 会在更新后会被立即调用,首次渲染不会执行此方法。
组件更新后,可以在此处对 DOM 进行操作:
componentWillUnmount() 会在组件卸载及销毁之前直接调用。
在此方法中执行必要的清理操作;
清除 timer
取消网络请求
清除在 componentDidMount() 中创建的订阅等
不常用的生命周期函数可自行阅读文档:
https://reactjs.org/docs/state-and-lifecycle.html
function Header(){
return(
<div>
Header内容
</div>
)
}
function Content(){
return(
<div>
<ul>
<li>列表内容1</li>
<li>列表内容2</li>
<li>列表内容3</li>
<li>列表内容4</li>
<li>列表内容5</li>
</ul>
</div>
)
}
function Footer(){
return(
<div>
Footer内容
</div>
)
}
为了更好的复用和维护组件,在组件化开发思想指导下需要对一个总的项目进行组件拆分,组件之间存在一个类似于父子的从属关系
上面的嵌套逻辑如下,它们存在如下关系
App组件是Header、Content、Footer组件的父组件
相应的,Header、Content、Footer就是App的子组件
在开发过程中,会经常遇到需要组件之间相互进行通信:
如App可能使用了多个Header,每个地方的Header展示的内容不同,那么我们就需要使用者传递给Header一些数据,让其进行展示
如我们在Main中一次性请求了Banner数据和List数据,那么就需要传递给他们来进行展示
也可能是子组件中发生了事件,需要由父组件来完成某些操作,那就需要子组件向父组件传递事件
总之,在一个React项目中,组件之间的通信是非常重要的环节
父组件通过 属性=值 的形式来传递给子组件数据
子组件通过 props 参数获取父组件传递过来的数据
类组件传递:
import React,{
Component } from "react";
import Child from "./Child"
Child.js
export default class App extends Component{
constructor(props){
super(props);
this.state = {
}
}
render(){
return (
<div>
<Child name="d1" age={
23} height={
1.86}/>//如果使用双引号传递数值,会被作为字符串处理
<Child name="d2" age={
20} height={
1.88}/>//传入Number参数需要大括号
</div>
)
}
}
App.js
import React,{
Component } from "react";
export default class Child extends Component{
constructor(props){
super(props);
this.props = props;
}
render(){
const {
name, age, height } = this.props;
return(
<div>
<h2>我是子组件</h2>
<p>传过来的信息有{
name + ","+ age + "," + height}</p>
</div>
)
}
}
函数组件传递:
App.js
import React,{
Component } from "react";
import Child from "./Child"
export default class App extends Component{
constructor(props){
super(props);
this.state = {
}
}
render(){
return (
<div>
<Child name="d3" age={
18} height={
1.68}/>
<Child name="d4" age={
19} height={
1.65}/>
</div>
)
}
}
Child.js
import React,{
Component } from "react";
export default function Child(props){
const {
name,age,height} = props;
return (
<div>
<h2>我是子组件</h2>
<p>传过来的信息有{
name + ","+ age + "," + height}</p>
</div>
)
}
对于传递给子组件的数据,有时候我们可能希望进行验证,特别是对于大型项目来说
当然,如果你项目中默认继承了Flow或者TypeScript,那么直接就可以进行类型验证
但是,即使我们没有使用Flow或者TypeScript,也可以通过 prop-types 库来进行参数验证
首先在顶部进行引用
再对子组件进行相关配置,例如:
更多的验证方式可以参照官网介绍
如果没有传递,我们希望有默认值,就可以使用defaultProps,代码结构如下:
在App.js中调用的时候,不需要在子组件的标签里面传入任何参数,得到的结果如下:
当然,能够检查或是设置默认值的参数类型还很多,这儿不多赘述。
以上就是React组件化开发内容的第一部分,内容量比较大,望能够慢慢吸收。。
感谢coderwhy(王红元老师)的课程内容
爱生活,爱猪猪