目录
2.1 componentWillReceiveProps ()
3.1 新旧生命周期相同:componentWillUnmount()
一、组件生命周期
React 组件的生命周期函数,又叫钩子函数,它能响应不同的状态。
组件的生命周期可分成三个状态:
- Mounting:已插入真实 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实 DOM
生命周期的方法有:
-
componentWillMount 在渲染前调用,在客户端也在服务端。
-
componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异部操作阻塞UI)。
-
componentWillReceiveProps 在组件接收到一个新的prop时被调用。这个方法在初始化render时不会被调用。
-
shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
可以在你确认不需要更新组件时使用。 -
componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
-
componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。
-
componentWillUnmount在组件从 DOM 中移除的时候立刻被调用。
挂载时:先执行构造器(constructor)=》组件将要挂载(componentWillMount)=》组件挂载渲染(render)=》组件挂载完成(componentDidMount)=》组件销毁(componentWillUnmount)
组件内部状态更新:组件是否应该更新(shouldComponentUpdate)=》组件将要更新(componentWillUpdate)=》组件更新渲染(render)=》组件更新完成(componentDidUpdate)
强制更新:调用this.forceUpdate(),这个api和setState一样都是react自带的,一般这个强制更新很少用,它的执行流程就是比上述的正常更新流程少一步询问是否更新(shouldComponentUpdate)
父组件重新render:调用组件将要接收新props(componentWillReceiveProps)=》组件是否应该更新(shouldComponentUpdate)=》组件将要更新(componentWillUpdate)=》组件更新渲染(render)=》组件更新完成(componentDidUpdate)
1.1 constructor
数据的初始化
接收props和context,当想在函数内使用这两个参数需要在super传入参数,当使用constructor时必须使用super,否则可能会有this的指向问题,如果不初始化state或者不进行方法绑定,则可以不为组件实现构造函数;
1.2 componentWillMount
在挂载之前也就是render之前被调用。
在服务端渲染唯一会调用的函数,代表已经初始化数据但是没有渲染dom,因此在此方法中同步调用 setState()
不会触发额外渲染。
1.3 getDerivedStateFromProps
从props获取state。
替代了componentWillReceiveProps,
此方法适用于罕见的用例,即 state 的值在任何时候都取决于 props。
在初始挂载和后续更新时都会被调用,返回一个对象更新state,如果返回null就不更新;
如果props传入的内容不需要影响到你的state,那么就需要返回一个null,这个返回值是必须的,所以尽量将其写到函数的末尾。
static getDerivedStateFromProps(nextProps, prevState) {
const {type} = nextProps;
// 当传入的type发生变化的时候,更新state
if (type !== prevState.type) {
return {
type,
};
}
// 否则,对于state不进行任何操作
return null;
}
1.4 render
class组件中唯一必须实现的方法。
render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。
当render被调用时,他会检查this.props.和this.state的变化并返回以下类型之一:
- 通过jsx创建的react元素
- 数组或者fragments:使得render可以返回多个元素
- Portals:可以渲染子节点到不同的dom树上
- 字符串或数值类型:他们在dom中会被渲染为文本节点
- 布尔类型或者null:什么都不渲染
注意
render函数是纯函数,这意味着在不修改组件state的情况下,每次调用都应该返回相同的结果,并且它不会直接和浏览器交互,如果需要和浏览器交互需要在componentDidMunt函数中或者其他生命周期函数中执行操作。
更新过程如果
shouldComponentUpdate()
返回 false,则不会调用render()
。
1.5 componentDidMount
在组件挂在后(插入到dom树中)后立即调用
可以在这里调用Ajax请求,返回的数据可以通过setState使组件重新渲染,或者添加订阅,但是要在conponentWillUnmount中取消订阅
2.1 componentWillReceiveProps ()
在已挂载的组件接收新的props之前调用。
通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件,可以在此方法中使用this.setState改变state。
2.2 shouldComponentUpdate
在渲染之前被调用,默认返回为true。
返回值是判断组件的输出是否受当前state或props更改的影响,默认每次state发生变化都重新渲染,首次渲染或使用forceUpdate时不被调用。
他主要用于性能优化,会对 props 和 state 进行浅层比较,并减少了跳过必要更新的可能性。不建议深层比较,会影响性能。如果返回false,则不会调用componentWillUpdate、render和componentDidUpdate
- 唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新,但是不建议,建议使用 PureComponent
- 因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断
2.3 componentWillUpdate
当组件接收到新的props和state会在渲染前调用,初始渲染不会调用该方法。
shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,不能在这使用setState,在函数返回之前不能执行任何其他更新组件的操作
此方法可以替换为
componentDidUpdate()
。如果你在此方法中读取 DOM 信息(例如,为了保存滚动位置),则可以将此逻辑移至getSnapshotBeforeUpdate()
中。
2.4 getSnapshotBeforeUpdate
在最后一次渲染(提交到dom节点)之前调用,替换componetnWillUpdate
它能在组件更改之前获取dom的节点信息(滚动位置),该方法所有返回值都会作为参数传给componentDidUpdate
和componentWillUpdate的区别
- 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在
componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。- getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。
2.5 componentDidUpdate
在更新之后立即调用,首次渲染不会调用,之后每次重新渲染都会被调用。
可以在该方法调用setState,但是要包含在条件语句中,否则一直更新会造成死循环
当组件更新后,可以在此处对 DOM 进行操作。如果对更新前后的props进行了比较,可以进行网络请求。(当 props 未发生变化时,则不会执行网络请求)。
componentDidUpdate(prevProps) {
// 典型用法(不要忘记比较 props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
如果组件实现了
getSnapshotBeforeUpdate()
生命周期(不常用),则它的返回值将作为componentDidUpdate()
的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined。如果返回false就不会调用这个函数。
3.1 新旧生命周期相同:componentWillUnmount()
在组件卸载和销毁之前调用
在这执行必要的清理操作,例如,清除timer(setTimeout,setInterval),取消网络请求,或者取消在componentDidMount的订阅,移除所有监听
有时候我们会碰到这个warning:
Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.
原因:因为你在组件中的ajax请求返回setState,而你组件销毁的时候,请求还未完成,因此会报warning
二、组件复用Render Props
1.组件复用
React 组件复用是一种通过创建可重复使用的组件来提高代码效率和可维护性的技术。这种方法使您可以将功能相似或相同的部分封装在一个组件中,然后在整个应用程序中多次使用它们,而不需要重复编写相同的代码。这有助于减少代码量,降低错误的可能性,提高开发速度,并使代码更易于理解和维护。
以下是一些实现 React 组件复用的方法:
- 函数组件:使用函数组件来创建可重用的 UI 组件。您可以将组件的输入作为 props 传递给组件,并根据这些 props 渲染不同的内容。
- 类组件:您也可以使用类组件来创建可重用的组件,类似于函数组件,但具有更多的生命周期方法和状态管理能力。
- 高阶组件 (Higher-Order Components,HOC):HOC 是一种用于增强组件功能的模式。它们是函数,接受一个组件并返回一个新组件,可以添加额外的 props 或功能。
- Render Props:Render Props 是一种模式,通过将一个函数作为 prop 传递给组件,允许组件的用户决定如何渲染组件的一部分。
- Hooks:使用 React Hooks,您可以在函数组件中添加状态和生命周期功能,以实现组件复用。
2. Render Props
Render Props 组件是一个接受一个函数作为 prop 并使用该函数来渲染内容的组件。通常,该函数将一些数据或操作作为参数,并返回一个 React 元素。这个函数会在 Render Props 组件的 render
方法中被调用。
创建一个包含 Render Props 的组件:首先,您需要创建一个包含 Render Props 的组件。这个组件应该接受一个函数作为 prop,通常命名为 render
、children
或其他描述性名称。
class MouseTracker extends React.Component {
// ...
render() {
return (
<div onMouseMove={/* Logic for tracking mouse position */}>
{/* Call the render function with data */}
{this.props.render(this.state.x, this.state.y)}
</div>
);
}
}
使用 Render Props 组件:当使用 Render Props 组件时,您将一个函数作为子组件传递给它,通常使用 render
prop。这个函数接受来自 Render Props 组件的数据,您可以在其中定义要渲染的内容。
class App extends React.Component {
renderMousePosition(x, y) {
// 渲染鼠标位置信息
return <p>Mouse position: ({x}, {y})</p>;
}
render() {
return (
<MouseTracker render={this.renderMousePosition} />
);
}
}
这个示例中,App
组件将 renderMousePosition
函数传递给 MouseTracker
组件,并在 MouseTracker
内部的 render
方法中调用它,传递鼠标的 x 和 y 坐标,然后将结果渲染出来。
render prop 是一个用于告知组件需要渲染什么内容的函数 prop。
这项技术使我们共享状态或行为非常容易。要获得这个状态或行为,只要渲染一个带有render prop的组件就能够告诉它当前要渲染什么。
Render Props 也可以与 React Hooks 结合使用,使其更加强大和简洁。这是一个强大的模式,用于构建可重用和高度定制的组件。
三、高阶组件
React 高阶组件(Higher-Order Component,HOC)是一种高度可复用的模式,用于增强或包装现有 React 组件的功能。它本质上是一个函数,接受一个组件作为参数,并返回一个新的增强型组件。HOC 可以用于在不修改原始组件代码的情况下,添加一些额外的功能或逻辑。
以下是一些关于 React 高阶组件的重要概念和用法:
-
接受组件作为参数:HOC 接受一个或多个 React 组件作为输入,并返回一个新的 React 组件。这意味着您可以将一个或多个组件传递给 HOC,并将它们包装在一个新的组件中。
-
增强功能:HOC 的主要目的是增强组件的功能。这可以包括添加状态、添加生命周期方法、处理数据、访问上下文等。
-
透明传递 props:通常,HOC 会将所有传递给它的 props 原封不动地传递给包装的组件。这确保了原始组件不会意识到它被包装了,并且可以继续接收它需要的 props。
-
命名约定:通常,HOC 的名称以 "with" 开头,以表示它是一个高阶组件。例如,
withAuth
可能是一个添加身份验证功能的 HOC。
高阶组件的编写和调用过程类似于下面这样:
// 定义一个高阶组件
// 1.高阶组件会接收一个组件作为参数
function hoc(Cpn) {
class NewCpn extends PureComponent {
render() {
return <Cpn/>
}
}
// 2.并且返回一个新的组件
return NewCpn
}
// 创建一个组件作为参数
class HelloWorld extends PureComponent {
render() {
return <h2>Hello World</h2>
}
}
// 调用高阶组件会返回一个新的组件
const HelloWorldHOC = hoc(HelloWorld)
export class App extends PureComponent {
render() {
return (
<div>
{/* 返回的新组件展示到App组件中 */}
<HelloWorldHOC/>
</div>
)
}
}
高阶组件并不是React API的一部分,它是基于React的 组合特性而形成的设计模式;
以下是一个简单的示例,演示如何创建一个高阶组件:
// 高阶组件,用于添加身份验证功能
function withAuth(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = { isAuthenticated: false };
}
componentDidMount() {
// 在实际应用中可能会进行身份验证
this.setState({ isAuthenticated: true });
}
render() {
// 通过props将isAuthenticated传递给被包装的组件
return (
<WrappedComponent
{...this.props}
isAuthenticated={this.state.isAuthenticated}
/>
);
}
};
}
// 使用高阶组件包装一个组件
const Profile = ({ isAuthenticated }) => {
return isAuthenticated ? (
<div>Welcome to your profile!</div>
) : (
<div>Please log in to view your profile.</div>
);
};
const AuthProfile = withAuth(Profile);
在这个示例中,withAuth
是一个高阶组件,它接受一个组件作为参数,并返回一个包装了身份验证逻辑的新组件。通过这种方式,我们可以轻松地将身份验证功能添加到任何需要的组件中,而不需要修改原始组件的代码。
四、React路由
React 路由(React Router)是一个用于管理单页面应用(SPA)中页面导航和路由的库。它允许您在 React 应用程序中创建多个页面、导航链接以及根据 URL 进行页面切换,同时保持页面刷新最小化,以提供更流畅的用户体验。React 路由通常与 React 应用程序一起使用,以帮助组织和管理不同页面之间的视图和状态。
1.1 SPA理解
- 单页面应用(single page web application SPA)
- 整个页面只有一个完整的页面(html文件)
- 点击页面的链接不会刷新页面,只会做页面的局部更新
- 数据都需要通过ajax请求获取,并在前端异步展示
1.2 什么是路由?
- 一个路由就是一个映射关系
- key永远为路径,value可能是function或者component
1.3 路由分类
1.后端路由:
- 理解: value是function, 用来处理客户端提交的请求。
- 注册路由: router.get(path, function(req, res))
- 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
2.前端路由:
- 浏览器端路由,value是component,用于展示页面内容。
- 注册路由: <Route path="/test" component={Test}>
- 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
2. react-router-dom的理解
- react的一个插件库。
- 专门用来实现一个SPA应用。
- 9基于react的项目基本都会用到此库。
2.1 内置组件
<BrowserRouter>
<HashRouter>
<Route>
<Redirect>
<Link>
<NavLink>
<Switch>
2.2 路由组件与一般组件
import React, { Component } from 'react';
import { Link,Route } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Header from './components/Header'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap'
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header a={1}></Header> {/* 一般组件写法 */}
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html 要靠<a>跳转不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件 */}
<Link className="list-group-item" to="/home">Home</Link>
<Link className="list-group-item" to="/about">About</Link>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 路由组件写法 */}
<Route path="/home" component={Home}/>
<Route path="/about" component={About}/>
</div>
</div>
</div>
</div>
</div>
)
}
}
Header 组件代码:
import React, { Component } from 'react'
export default class Header extends Component {
render() {
console.log('一般组件',this.props)
return (
<div className="page-header"><h2>React Router Demo</h2></div>
)
}
}
路由组件与一般组件的不同之处
1.写法不同:
一般组件: <Demo/>
路由组件: <Route path="/demo" component={Demo}/>
2.存放位置不同:
一般组件: components
路由组件: pages
3.接收到的props不同:
一般组件: 写组件标签时传递了什么,就能收到什么
路由组件: 接收带三个固定的属性
history:
go: ? go(n)
goBack: ? goBack()
goForward: ? goForward()
push: ? push(path, state)
replace: ? replace(path, state)
location:
pathname: "/home"
search: ""
state: undefined
match:
params: {}
path: "/home"
url: "/home"
五、Axios
Axios 是一个用于在 JavaScript 应用程序中进行 HTTP 请求的流行库,通常在 React 项目中用于与后端服务器通信。Axios 提供了一种简单且强大的方式来执行异步的 HTTP 请求,包括 GET、POST、PUT、DELETE 等。
1. 安装 Axios
首先,您需要在项目中安装 Axios。您可以使用 npm 或 yarn 来安装它。
npm install axios
# 或者
yarn add axios
2. 导入 Axios
在您的 React 组件文件中,导入 Axios。
import axios from 'axios';
3. 执行 HTTP 请求
使用 Axios 来执行 HTTP 请求。以下是一些常见的示例:
- 发送 GET 请求:
axios.get('https://api.example.com/data')
.then(response => {
// 处理成功的响应
console.log(response.data);
})
.catch(error => {
// 处理请求错误
console.error(error);
});
- 发送 POST 请求:
axios.post('https://api.example.com/post-data', { data: 'some data' })
.then(response => {
// 处理成功的响应
console.log(response.data);
})
.catch(error => {
// 处理请求错误
console.error(error);
});
- 发送 PUT 请求:
axios.put('https://api.example.com/update-data/1', { updatedData: 'new data' })
.then(response => {
// 处理成功的响应
console.log(response.data);
})
.catch(error => {
// 处理请求错误
console.error(error);
});
- 发送 DELETE 请求:
axios.delete('https://api.example.com/delete-data/1')
.then(response => {
// 处理成功的响应
console.log(response.data);
})
.catch(error => {
// 处理请求错误
console.error(error);
});
4. 处理响应
Axios 通过 Promise 提供异步的响应处理。您可以使用 .then()
处理成功的响应,使用 .catch()
处理请求错误。您还可以在请求中使用 .finally()
来执行无论请求成功还是失败都需要执行的操作。
5. 配置全局设置
您可以在项目的某个地方配置全局的 Axios 设置,例如设置基本 URL、默认请求头等。这可以通过创建 Axios 实例来实现。
const axiosInstance = axios.create({
baseURL: 'https://api.example.com',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123',
},
});
axiosInstance.get('/data')
.then(response => {
// 处理响应
})
.catch(error => {
// 处理错误
});
使用 async/await:您还可以使用 async/await
语法来更方便地处理异步请求。
async function fetchData() {
try {
const response = await axios.get('https://api.example.com/data');
// 处理响应
console.log(response.data);
} catch (error) {
// 处理错误
console.error(error);
}
}
拦截器:Axios 允许您设置拦截器,以在请求或响应之前或之后执行自定义逻辑。这可以用于添加全局的请求头、处理认证、日志记录等操作。