0

0

React高阶组件使用详解

php中世界最好的语言

php中世界最好的语言

发布时间:2018-05-24 15:24:11

|

2015人浏览过

|

来源于php中文网

原创

高阶组件的定义类比于高阶函数的定义。高阶函数接收函数作为参数,并且返回值也是一个函数。类似的,高阶组件接收React组件作为参数,并且返回一个新的React组件。高阶组件本质上也是一个函数,并不是一个组件,这一点一定不要弄错。

为什么React引入高阶组件的概念?它到底有何威力?让我们先通过一个简单的例子说明一下。

假设有一个组件MyComponent,需要从LocalStorage中获取数据,然后渲染数据到界面。我们可以这样写组件代码:

import React, { Component } from 'react'
class MyComponent extends Component {
  componentWillMount() {
      let data = localStorage.getItem('data');
      this.setState({data});
  }
  
  render() {
    return <p>{this.state.data}</p>
  }
}

代码很简单,但当有其他组件也需要从LocalStorage中获取同样的数据展示出来时,需要在每个组件都重复componentWillMount中的代码,这显然是很冗余的。下面让我们来看看使用高阶组件可以怎么改写这部分代码。

import React, { Component } from 'react'
function withPersistentData(WrappedComponent) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem('data');
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
}
const MyComponentWithPersistentData = withPersistentData(MyComponent2)

withPersistentData就是一个高阶组件,它返回一个新的组件,在新组件的componentWillMount中统一处理从LocalStorage中获取数据的逻辑,然后将获取到的数据以属性的方式传递给被包装的组件WrappedComponent,这样在WrappedComponent中就可以直接使用this.props.data获取需要展示的数据了,如MyComponent2所示。当有其他的组件也需要这段逻辑时,继续使用withPersistentData这个高阶组件包装这些组件就可以了。

通过这个例子,可以看出高阶组件的主要功能是封装并分离组件的通用逻辑,让通用逻辑在组件间更好地被复用。高阶组件的这种实现方式,本质上是一个装饰者设计模式。

高阶组件的参数并非只能是一个组件,它还可以接收其他参数。例如,组件MyComponent3需要从LocalStorage中获取key等于name的数据,而不是上面例子中写死的key等于data的数据,withPersistentData这个高阶组件就不满足我们的需求了。我们可以让它接收额外的一个参数,来决定从LocalStorage中获取哪个数据:

import React, { Component } from 'react'
function withPersistentData(WrappedComponent, key) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData(MyComponent2, 'data');
const MyComponent3WithPersistentData = withPersistentData(MyComponent3, 'name');

新版本的withPersistentData就满足我们获取不同key的值的需求了。高阶组件中的参数当然也可以是函数,我们将在下一节进一步说明。

3. 进阶用法

高阶组件最常见的函数签名形式是这样的:

HOC([param])([WrappedComponent])

用这种形式改写withPersistentData,如下:

import React, { Component } from 'react'
const withPersistentData = (key) => (WrappedComponent) => {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData('data')(MyComponent2);
const MyComponent3WithPersistentData = withPersistentData('name')(MyComponent3);

实际上,此时的withPersistentData和我们最初对高阶组件的定义已经不同。它已经变成了一个高阶函数,但这个高阶函数的返回值是一个高阶组件。HOC([param])([WrappedComponent])这种形式中,HOC([param])才是真正的高阶组件,我们可以把它看成高阶组件的变种形式。这种形式的高阶组件因其特有的便利性——结构清晰(普通参数和被包裹组件分离)、易于组合,大量出现在第三方库中。如react-redux中的connect就是一个典型。connect的定义如下:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(WrappedComponent)

这个函数会将一个React组件连接到Redux 的 store。在连接的过程中,connect通过函数类型的参数mapStateToProps,从全局store中取出当前组件需要的state,并把state转化成当前组件的props;同时通过函数类型的参数mapDispatchToProps,把当前组件用到的Redux的action creators,以props的方式传递给当前组件。

例如,我们把组件ComponentA连接到Redux上的写法类似于:

const ConnectedComponentA = connect(mapStateToProps, mapDispatchToProps)(ComponentA);

我们可以把它拆分来看:

// connect 是一个函数,返回值enhance也是一个函数
const enhance = connect(mapStateToProps, mapDispatchToProps);
// enhance是一个高阶组件
const ConnectedComponentA = enhance(ComponentA);

当多个函数的输出和它的输入类型相同时,这些函数是很容易组合到一起使用的。例如,有f,g,h三个高阶组件,都只接受一个组件作为参数,于是我们可以很方便的嵌套使用它们:f( g( h(WrappedComponent) ) )。这里可以有一个例外,即最内层的高阶组件h可以有多个参数,但其他高阶组件必须只能接收一个参数,只有这样才能保证内层的函数返回值和外层的函数参数数量一致(都只有1个)。

例如我们将connect和另一个打印日志的高阶组件withLog联合使用:

const ConnectedComponentA = connect(mapStateToProps)(withLog(ComponentA));

这里我们定义一个工具函数:compose(...functions),调用compose(f, g, h) 等价于 (...args) => f(g(h(...args)))。用compose函数我们可以把高阶组件嵌套的写法打平:

const enhance = compose(
  connect(mapStateToProps),
  withLog
);
const ConnectedComponentA = enhance(ComponentA);

像Redux等很多第三方库都提供了compose的实现,compose结合高阶组件使用,可以显著提高代码的可读性和逻辑的清晰度。

4.与父组件区别

有些同学可能会觉得高阶组件有些类似父组件的使用。例如,我们完全可以把高阶组件中的逻辑放到一个父组件中去执行,执行完成的结果再传递给子组件。从逻辑的执行流程上来看,高阶组件确实和父组件比较相像,但是高阶组件强调的是逻辑的抽象。高阶组件是一个函数,函数关注的是逻辑;父组件是一个组件,组件主要关注的是UI/DOM。如果逻辑是与DOM直接相关的,那么这部分逻辑适合放到父组件中实现;如果逻辑是与DOM不直接相关的,那么这部分逻辑适合使用高阶组件抽象,如数据校验、请求发送等。

5. 注意事项

1)不要在组件的render方法中使用高阶组件,尽量也不要在组件的其他生命周期方法中使用高阶组件。因为高阶组件每次都会返回一个新的组件,在render中使用会导致每次渲染出来的组件都不相等(===),于是每次render,组件都会卸载(unmount),然后重新挂载(mount),既影响了效率,又丢失了组件及其子组件的状态。高阶组件最适合使用的地方是在组件定义的外部,这样就不会受到组件生命周期的影响了。

2)如果需要使用被包装组件的静态方法,那么必须手动拷贝这些静态方法。因为高阶组件返回的新组件,是不包含被包装组件的静态方法。hoist-non-react-statics可以帮助我们方便的拷贝组件所有的自定义静态方法。有兴趣的同学可以自行了解。

3)Refs不会被传递给被包装组件。尽管在定义高阶组件时,我们会把所有的属性都传递给被包装组件,但是ref并不会传递给被包装组件。如果你在高阶组件的返回组件中定义了ref,那么它指向的是这个返回的新组件,而不是内部被包装的组件。如果你希望获取被包装组件的引用,你可以把ref回调函数定义成一个普通属性(给它一个ref以外的名字)。下面的例子就用inputRef这个属性名代替了常规的ref命名:

function FocusInput({ inputRef, ...rest }) {
  return <input ref={inputRef} {...rest} />;
}
//enhance 是一个高阶组件
const EnhanceInput = enhance(FocusInput);
// 在一个组件的render方法中...
return (<EnhanceInput 
  inputRef={(input) => {
    this.input = input
  }
}>)
// 让FocusInput自动获取焦点
this.input.focus();

下篇预告:

React 深入系列7:React 常用模式


我的新书《React进阶之路》已上市,请大家多多支持!
链接:京东 当当

图片描述

React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。

1. 基本概念

高阶组件是React 中一个很重要且比较复杂的概念,高阶组件在很多第三方库(如Redux)中都被经常使用。在项目中用好高阶组件,可以显著提高代码质量。

高阶组件的定义类比于高阶函数的定义。高阶函数接收函数作为参数,并且返回值也是一个函数。类似的,高阶组件接收React组件作为参数,并且返回一个新的React组件。高阶组件本质上也是一个函数,并不是一个组件,这一点一定不要弄错。

2. 应用场景

为什么React引入高阶组件的概念?它到底有何威力?让我们先通过一个简单的例子说明一下。

假设有一个组件MyComponent,需要从LocalStorage中获取数据,然后渲染数据到界面。我们可以这样写组件代码:

import React, { Component } from 'react'
class MyComponent extends Component {
  componentWillMount() {
      let data = localStorage.getItem('data');
      this.setState({data});
  }
  
  render() {
    return <p>{this.state.data}</p>
  }
}

代码很简单,但当有其他组件也需要从LocalStorage中获取同样的数据展示出来时,需要在每个组件都重复componentWillMount中的代码,这显然是很冗余的。下面让我们来看看使用高阶组件可以怎么改写这部分代码。

import React, { Component } from 'react'
function withPersistentData(WrappedComponent) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem('data');
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
}
const MyComponentWithPersistentData = withPersistentData(MyComponent2)

withPersistentData就是一个高阶组件,它返回一个新的组件,在新组件的componentWillMount中统一处理从LocalStorage中获取数据的逻辑,然后将获取到的数据以属性的方式传递给被包装的组件WrappedComponent,这样在WrappedComponent中就可以直接使用this.props.data获取需要展示的数据了,如MyComponent2所示。当有其他的组件也需要这段逻辑时,继续使用withPersistentData这个高阶组件包装这些组件就可以了。

通过这个例子,可以看出高阶组件的主要功能是封装并分离组件的通用逻辑,让通用逻辑在组件间更好地被复用。高阶组件的这种实现方式,本质上是一个装饰者设计模式。

高阶组件的参数并非只能是一个组件,它还可以接收其他参数。例如,组件MyComponent3需要从LocalStorage中获取key等于name的数据,而不是上面例子中写死的key等于data的数据,withPersistentData这个高阶组件就不满足我们的需求了。我们可以让它接收额外的一个参数,来决定从LocalStorage中获取哪个数据:

import React, { Component } from 'react'
function withPersistentData(WrappedComponent, key) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData(MyComponent2, 'data');
const MyComponent3WithPersistentData = withPersistentData(MyComponent3, 'name');

新版本的withPersistentData就满足我们获取不同key的值的需求了。高阶组件中的参数当然也可以是函数,我们将在下一节进一步说明。

3. 进阶用法

高阶组件最常见的函数签名形式是这样的:

HOC([param])([WrappedComponent])

用这种形式改写withPersistentData,如下:

import React, { Component } from 'react'
const withPersistentData = (key) => (WrappedComponent) => {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData('data')(MyComponent2);
const MyComponent3WithPersistentData = withPersistentData('name')(MyComponent3);

实际上,此时的withPersistentData和我们最初对高阶组件的定义已经不同。它已经变成了一个高阶函数,但这个高阶函数的返回值是一个高阶组件。HOC([param])([WrappedComponent])这种形式中,HOC([param])才是真正的高阶组件,我们可以把它看成高阶组件的变种形式。这种形式的高阶组件因其特有的便利性——结构清晰(普通参数和被包裹组件分离)、易于组合,大量出现在第三方库中。如react-redux中的connect就是一个典型。connect的定义如下:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(WrappedComponent)

这个函数会将一个React组件连接到Redux 的 store。在连接的过程中,connect通过函数类型的参数mapStateToProps,从全局store中取出当前组件需要的state,并把state转化成当前组件的props;同时通过函数类型的参数mapDispatchToProps,把当前组件用到的Redux的action creators,以props的方式传递给当前组件。

例如,我们把组件ComponentA连接到Redux上的写法类似于:

const ConnectedComponentA = connect(mapStateToProps, mapDispatchToProps)(ComponentA);

我们可以把它拆分来看:

// connect 是一个函数,返回值enhance也是一个函数
const enhance = connect(mapStateToProps, mapDispatchToProps);
// enhance是一个高阶组件
const ConnectedComponentA = enhance(ComponentA);

当多个函数的输出和它的输入类型相同时,这些函数是很容易组合到一起使用的。例如,有f,g,h三个高阶组件,都只接受一个组件作为参数,于是我们可以很方便的嵌套使用它们:f( g( h(WrappedComponent) ) )。这里可以有一个例外,即最内层的高阶组件h可以有多个参数,但其他高阶组件必须只能接收一个参数,只有这样才能保证内层的函数返回值和外层的函数参数数量一致(都只有1个)。

例如我们将connect和另一个打印日志的高阶组件withLog联合使用:

const ConnectedComponentA = connect(mapStateToProps)(withLog(ComponentA));

这里我们定义一个工具函数:compose(...functions),调用compose(f, g, h) 等价于 (...args) => f(g(h(...args)))。用compose函数我们可以把高阶组件嵌套的写法打平:

const enhance = compose(
  connect(mapStateToProps),
  withLog
);
const ConnectedComponentA = enhance(ComponentA);

像Redux等很多第三方库都提供了compose的实现,compose结合高阶组件使用,可以显著提高代码的可读性和逻辑的清晰度。

4.与父组件区别

有些同学可能会觉得高阶组件有些类似父组件的使用。例如,我们完全可以把高阶组件中的逻辑放到一个父组件中去执行,执行完成的结果再传递给子组件。从逻辑的执行流程上来看,高阶组件确实和父组件比较相像,但是高阶组件强调的是逻辑的抽象。高阶组件是一个函数,函数关注的是逻辑;父组件是一个组件,组件主要关注的是UI/DOM。如果逻辑是与DOM直接相关的,那么这部分逻辑适合放到父组件中实现;如果逻辑是与DOM不直接相关的,那么这部分逻辑适合使用高阶组件抽象,如数据校验、请求发送等。

5. 注意事项

1)不要在组件的render方法中使用高阶组件,尽量也不要在组件的其他生命周期方法中使用高阶组件。因为高阶组件每次都会返回一个新的组件,在render中使用会导致每次渲染出来的组件都不相等(===),于是每次render,组件都会卸载(unmount),然后重新挂载(mount),既影响了效率,又丢失了组件及其子组件的状态。高阶组件最适合使用的地方是在组件定义的外部,这样就不会受到组件生命周期的影响了。

2)如果需要使用被包装组件的静态方法,那么必须手动拷贝这些静态方法。因为高阶组件返回的新组件,是不包含被包装组件的静态方法。hoist-non-react-statics可以帮助我们方便的拷贝组件所有的自定义静态方法。有兴趣的同学可以自行了解。

3)Refs不会被传递给被包装组件。尽管在定义高阶组件时,我们会把所有的属性都传递给被包装组件,但是ref并不会传递给被包装组件。如果你在高阶组件的返回组件中定义了ref,那么它指向的是这个返回的新组件,而不是内部被包装的组件。如果你希望获取被包装组件的引用,你可以把ref的回调函数定义成一个普通属性(给它一个ref以外的名字)。下面的例子就用inputRef这个属性名代替了常规的ref命名:

function FocusInput({ inputRef, ...rest }) {
  return <input ref={inputRef} {...rest} />;
}
//enhance 是一个高阶组件
const EnhanceInput = enhance(FocusInput);
// 在一个组件的render方法中...
return (<EnhanceInput 
  inputRef={(input) => {
    this.input = input
  }
}>)
// 让FocusInput自动获取焦点
this.input.focus();

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

react实现选中li高亮步骤详解

EasyCanvas绘图库在Pixeler项目开发中使用实战总结

Cardify卡片工坊
Cardify卡片工坊

使用Markdown一键生成精美的小红书知识卡片

下载

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

89

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

276

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

59

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

99

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

105

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

230

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

619

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

173

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6.1万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1万人学习

React核心原理新老生命周期精讲
React核心原理新老生命周期精讲

共12课时 | 1.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号