React
React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发。它以声明式编码、组件化架构和高效的更新机制为特点,广泛应用于单页应用程序(SPA)的开发。理解 React 的工作原理,有助于更有效地使用它构建高性能的 Web 应用。
设计理念:
- 快速响应
- 异步可中断(把一个任务切分成一个个子任务)
- 增量更新(只更新改动的内容)
React 框架内部的运作可以分为 3 层
- Virtual DOM 层,描述页面长什么样。
- Reconciler 层,负责调用组件生命周期方法,进行 Diff 运算等。
- Renderer 层,根据不同的平台,渲染出相应的页面,比较常见的是 ReactDOM 和 ReactNative。
下面是 React 的一些核心原理:
1.虚拟 DOM(Virtual DOM)
- 概念:虚拟 DOM 是一个轻量级的 JavaScript 对象,它是对真实 DOM 的抽象表示。React 使用虚拟 DOM 来描述 UI 的状态,这使得它能在内存中快速计算出 DOM 更新,而无需直接操作耗时的真实 DOM。
- 工作流程:
- 当组件状态发生变化时,React 会创建一个新的虚拟 DOM 树。
- 接着,React 通过对比新旧虚拟 DOM 树,来确定哪些地方发生了变化(这一过程称为“Diffing”)。
- 根据这些变化,React 会计算出最小的操作集来更新真实 DOM,这个过程称为“Reconciliation”。
2.组件(Components)
- 概念:组件是 React 应用的基本构建块,它们是封装了 UI 和行为的独立单元。React 组件可以是类组件或函数组件,支持内部状态(State)和属性(Props)。
- Props:Props 是组件对外的接口,用于从父组件接收数据。Props 是只读的,组件不应该修改接收到的 Props。
- State:State 是组件内部的状态,只能在组件内部管理和更新。当组件的状态改变时,React 会重新渲染该组件。
3.JSX
- 概念:JSX 是一种 JavaScript 的语法扩展,它允许在 JavaScript 代码中写类似 HTML 的标记。JSX 在构建过程中会被编译成标准的 JavaScript 对象和函数调用。
- 优势:JSX 使得代码更易读和写,同时保持了 JavaScript 的强大功能。
4.生命周期(Lifecycle)
- 概念:React 组件具有生命周期,这是一系列在组件存在期间会被自动调用的钩子函数。
- 用途:生命周期钩子可以用于在组件创建、更新和销毁时执行特定的操作,如获取数据、手动修改 DOM、清理资源等。
5. 高级特性
- 函数组件
- 非受控组件
- Portals
- context
- 异步组件
- 性能优化
- 高阶组件 HOC
- render Props
5.1 函数组件
- 纯函数,输入 props ,输出 jsx
- 没有实例,没有生命周期,没有 state
- 不能扩展其他方法
function List(props) {
let { list } = props
return <ul>
{
list.map(item => {
return <li key={item.id}>
<span>{item.value}</span>
</li>
})
}
</ul>
}
5.2 非受控组件
使用场景:
- 必须手动操作 DOM 元素,setState 实现不了
- 文件上传
- 某些富文本编辑器,需要传入 DOM 元素
5.3 Portal
使用场景:
- 子组件想要逃离父组件的限制 overflow:hidden
- 使用 Modal 组件
- 父组件的 z-index 值太小
- fixed 需要放置到 body 第一层级
class Modal {
render() {
return ReactDom.createPortal(
<div className ="modal">{this.props.children}</div>, document.body
)
}
}
5.4 Context
// 创建 context 对象,入参为默认值
const ThemeContext = React.createContext('light')
// 类组件消费 theme
class ThemeButton extends React.Component {
// 使用 ES6 静态方法指定
static contextType = ThemeContext
render() {
// 指定完 contextType 之后,就可以消费 theme
const theme = this.context
return (
<div>
<p>
button's theme is {theme}
</p>
</div>
)
}
}
// 也可以直接赋值
ThemeButton.contextType = ThemeContext // 消费前需要先指定 contextType
// 函数组件消费 theme
function ThemeLink () {
return (
<ThemeContext.Consumer>
{
value => <p>link's theme is {value}</p>
}
</ThemeContext.Consumer>
)
}
function ToolBar(props) {
return (
<div>
<ThemeButton />
<ThemeLink />
</div>
)
}
class Theme extends React.Component {
constructor() {
this.state = {
theme: "light"
}
}
render () {
return (
<!-- 生产 theme,供子组件消费 -->
<ThemeContext.Provider value={this.state.theme}>
<ToolBar />
</ThemeContext.Provider>
)
}
}
5.5 异步组件
- import()
- React.lazy
- React.Suspense
const Context = React.lazy(() => import('./Theme'))
class App extends React.Componenet {
constructor(props) {
super(props)
}
render() {
return (
<div>
<p>引入一个动态组件</p>
<!--未加载前,展示 fallback 内容-->
<React.Suspense fallback={<p>Loading...</p>}>
<!--加载成功,展示 context-->
<Context/>
</React.Suspense>
</div>
)
}
}
5.6 性能优化
- shouldComponentUpdate (简称 SCU)
- PureComponent 和 React.memo
shouldComponentUpdate
react 提供了一种使用者可以定制,组件是否更新的能力
shouldComponentUpdate(nextProps, nextState) {
if(nextState.count !== this.state.count) {
return true // 触发更新
}
return false // 不可渲染
}
使用场景:
当父组件更新时,子组件无需更新时,可以使用 (性能优化)
- 仅用于属性值的浅比较
class ChildComponent extends React.Component {
shouldComponentUpdate(nextProps,nextState) {
<!-- 默认值为 true -->
<!-- 只有当 props 中的 text 变更才触发更新 -->
<!-- 否则,子组件不随父组件更新而更新 -->
return nextState.text !== this.props.text
}
render() {
<!--子组件是静态文案-->
return <div>{text}</div>
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
}
componentDidMount() {
setInterval(() => {
this.setState({
count: this.state.count + 1
})
},1000)
}
render() {
<div>
<span>{this.props.count}</span>
<!-- 子组件文案固定 -->
<ChildComponent text={'hello'}/>
</div>
}
}
PureComponent & React.memo
- PureComponent
class Parent extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
count: 0
}
}
shouldComponentUpdate() {
/** 浅比较 */
}
render() {
<div>
<span>{this.props.count}</span>
</div>
}
}
- memo
适用于 函数组件
function MyComponent (props) {
/** 使用 props 渲染 */
}
function argEqual(prevProps, nextProps) {
/** 比较 prevProps 和 nextProps 结果值
* 如果相同,则不会更新,不相同更新
*/
}
export default React.memo(MyComponent, argEqual)
5.7 高阶组件
高阶组件是一种模式
// 形式上就是一种工厂模式
const HOCFactory = (Component) => {
return class HOC extends React.Component {
render() {
<!-- 返回拼装后的结果 -->
return <Component {...this.props} />
}
}
}
// 使用
const EnhanceComponentA = HOCFactory(ComponentA)
const EnhanceComponentB = HOCFactory(ComponentB)
实际用例
封装一个鼠标滑动事件
const withMouse = (Component) => {
return class HOC extends React.Component {
constructor(props) {
super(props)
this.state = {
x: 0,
y: 0
}
}
handleMouseMove(e) {
this.setState({
x: e.clientX,
y: e.clientY
})
}
render() {
<div onMouseMove={handleMouseMove}>
<Componnet {...this.props} mouse={this.state}/>
</div>
}
}
}
const App = (props) => {
const { x, y } = props.mouse
render() {
return <div>
x: {x}
y: {y}
</div>
}
}
实际用例2
redux connect 函数
import { connect } from 'react-redux'
// connect 是一个高阶组件
const VisibleToolList = connect(
mapStateToPorps,
mapDispatchToProps
)(ToolList)
export default VisibleToolList
redux connect 源码
export const connect = (mapStateToProps, mapDispatchToProps) => {
return (WrapComponent) => {
return class Connect extends React.Component {
constructor(props) {
super(props)
this.state = {
//... 省略props
}
}
render() {
// 省略若干逻辑
return <WrapComponent {...this.state} />
}
}
}
}
5.8 render props
class Mouse extends React.Componnet {
constructor(props) {
super(props)
this.state = {
x: 0,
y: 0
}
}
handleMouseMove(e) {
this.setState({
x: e.clientX,
y: e.clientY
})
}
render() {
return <div onMouseMove={handleMouseMove}>
{props.render(this.state)}
</div>
}
}
Mouse.propsType = {
render: PropTypes.func.required // 必须是一个函数
}
const App = () => {
render() {
return <Mouse render={({x,y}) => {
<h1>{x}, {y}</h1>
}} />
}
}
6.Hooks
- 概念:React Hooks 是 React 16.8 引入的一个特性,它允许在函数组件中使用状态和其他 React 特性,如 useState、useEffect 等。
- 优势:Hooks 使得函数组件更加强大和灵活,同时简化了代码结构,避免了类组件中常见的模板代码。
7.数据绑定
- React:采用单向数据流,组件的状态由 this.state 管理,并通过 setState 方法更新状态。对于父子组件间的通信,通过 props 传递数据和回调函数。
- Vue:默认支持双向数据绑定(尤其是在表单输入和应用状态之间),通过 v-model 指令实现。Vue 同样支持单向数据流,推荐在较大的应用中使用
Vuex
来管理应用状态。