React,Facebook开源的JavaScript库,用于构建高性能用户界面。通过组件化开发,它使UI的构建、维护变得简单高效。利用虚拟DOM实现快速渲染更新,适用于单页应用、移动应用(React Native)。React极大推动了现代前端技术的发展与实践。
本文详细介绍了 React 与 Vue 架构区别,以及ReactJSX、组件、Props、State、生命周期函数以及自React 16.8版本引入的Hooks等主要核心语法。
一、React 与 Vue 架构区别
React与Vue作为两大主流前端框架,在核心原理上有诸多相似之处,同时也存在显著差异,具体如下:
1. 共同点
- 组件化开发:两者均采用组件化思想构建用户界面,将复杂的UI拆分为可复用的小部件,有利于提高代码的可维护性和可重用性。
- Virtual DOM:React和Vue都利用虚拟DOM来提高页面渲染性能。通过在内存中维护一个DOM树的副本,计算出最小变更集,再应用到实际DOM中,减少实际DOM操作,加快页面响应速度。
- 数据驱动视图:两者都是数据驱动的框架,数据模型的变化会自动反映到视图上,无需手动操作DOM。
- 服务端渲染:都支持服务端渲染(Server Side Rendering, SSR),这有助于提升首次页面加载速度和SEO优化。
- 响应式更新:React和Vue都能根据状态变化自动更新UI,实现动态交互。
- 原生应用开发支持:React有React Native,Vue有Vue Native/Weex,两者都支持使用相同的开发语言和技术栈构建原生移动应用程序。
2. 不同点
-
数据绑定:
- Vue :支持双向数据绑定,开发者可以在模板中直接使用指令(如
v-model
)实现数据的自动同步,简化表单处理。 - React :采用单项数据流,数据从父组件流向子组件,状态更新需要显式调用
setState
等方法,强调数据流向的单一性和可预测性。
- Vue :支持双向数据绑定,开发者可以在模板中直接使用指令(如
-
模板与语法:
- Vue:使用基于HTML的模板语法,可以直接在模板中嵌入指令和表达式,更贴近传统网页开发者的习惯。
- React:采用JSX,一种将HTML-like结构与JavaScript混写的语法,使得组件的逻辑和结构紧密耦合,便于在JavaScript环境中直接操作虚拟DOM。
-
状态管理:
- Vue:内置了Vuex作为官方状态管理工具,提供集中式的状态存储和管理机制。
- React:虽然没有内置状态管理库,但社区广泛使用Redux、MobX等第三方库进行状态管理。
-
核心理念:
- Vue:倾向于提供一套完整的解决方案,包括路由、状态管理等,旨在降低前端开发的门槛。
- React:更像一个库而非框架,专注于视图层,鼓励开发者根据项目需求选择合适的工具链,因此在生态上更为灵活和分散。
-
生命周期与更新机制:
- React:使用Fiber作为更新机制,允许更细粒度地控制渲染过程,支持并发更新和暂停恢复渲染,提高了性能和用户体验。
- Vue:虽然也有自己的生命周期钩子和更新机制,但其更新策略相对直观和简单,对于初学者来说更容易理解。
综上所述,React和Vue虽在核心原理上有着相似的目标------提升开发效率和应用性能,但在实现细节、设计理念和生态系统上有各自的特点和优势,开发者可根据项目需求和个人喜好做出选择。
二、React 核心语法
React的核心语法主要包括以下几个方面:JSX、组件、Props、State、生命周期函数以及自React 16.8版本引入的Hooks。
1. JSX
React的核心语法之一是JSX,它是JavaScript和XML的一种混合语法,使得开发者能在JavaScript中以类似HTML的语法书写UI结构。以下是JSX的详细说明与示例:
1.1. JSX 基础
JSX允许你在JavaScript代码中直接编写看起来像HTML的标记,但实际上是JavaScript函数调用的语法糖。所有JSX最终会被转换成React.createElement()
函数调用来创建React元素。
示例:基础使用
jsx
function Welcome() {
return <h1>Hello, World!</h1>;
}
这段代码中,<h1>Hello, World!</h1>
就是JSX语法。它会被转换为:
jsx
React.createElement('h1', null, 'Hello, World!');
1.2. 属性(Props)
在JSX中,你可以给元素添加属性,就像在HTML中一样。在React中,这些属性被称为"props"。
jsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
name="Sara"
是传递给Welcome
组件的一个prop。
1.3. 表达式与JavaScript嵌入
你可以在JSX中嵌入JavaScript表达式,使用大括号{}
包裹。
jsx
function Welcome(props) {
return <h1>Hello, {props.name.toUpperCase()}</h1>;
}
1.4. 条件渲染
在React中,可以通过条件语句来控制组件的渲染。
jsx
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
return (
<div>
{isLoggedIn ? <p>Welcome back!</p> : <p>Please log in.</p>}
</div>
);
}
1.5. 循环渲染
React中可以使用JavaScript的循环来渲染列表数据。
jsx
function List(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>{number}</li>
);
return (
<ul>
{listItems}
</ul>
);
}
ReactDOM.render(<List numbers={[1, 2, 3, 4, 5]} />, document.getElementById('root'));
注释
JSX中可以使用标准的JavaScript注释,但需要放在大括号内。
jsx
return (
<div>
{/* 这是一个多行JSX注释 */}
<h1>Hello, {props.name}</h1>
</div>
);
JSX 规则
- JSX必须返回一个根元素。
- 标签名必须小写(HTML标签),大写开头的是自定义组件。
- 类名需要使用
className
代替class
。 - 属性值必须用引号包围,即使是数字。
- 自闭合标签需要闭合,如
<img src={src} alt="img" />
。
以上是React中JSX的一些基础概念和示例,它是构建React应用UI的基石。
2. 组件
React组件是React应用程序的基本构建块,它们帮助你将UI分解成独立、可复用的部分。组件可以接收输入(称为props)并返回React元素,描述应该在屏幕上看到什么。下面详细介绍React组件及其使用示例。
2.1. 组件类型
React中有两种主要类型的组件:函数组件和类组件。
函数组件
函数组件是最简单的组件形式,它是一个纯函数,接收props
作为参数并返回一个React元素。
jsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
类组件
类组件继承自React.Component
,包含局部状态(state)和生命周期方法。
jsx
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
2.2. Props
组件之间通过props传递信息。Props是只读的,不能在组件内部修改。
jsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 使用组件时传递props
const element = <Welcome name="Sara" />;
2.3. 状态(State)
只有类组件可以拥有自己的状态(state)。状态用于存储可能会随时间变化的数据,并且当状态变化时,组件会重新渲染。
jsx
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
incrementCount = () => {
this.setState(prevState => ({ count: prevState.count + 1 }));
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.incrementCount}>Increment</button>
</div>
);
}
}
2.4. 生命周期方法
React组件在不同的阶段会执行特定的方法,这些方法称为生命周期方法。不过,随着React 16.3版本及之后的更新,一些经典生命周期方法已被废弃,推荐使用新的生命周期方法和Hooks。
新的生命周期方法示例
componentDidMount
和componentDidUpdate
被合并为useEffect
Hook。getDerivedStateFromProps
和shouldComponentUpdate
可以用函数组件的逻辑直接替代。
示例:组合组件
组件可以嵌套其他组件,形成组件树。
jsx
function App() {
return (
<div>
<Welcome name="Alice" />
<Welcome name="Bob" />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
总结
React组件通过组合、props传递和状态管理来构建动态用户界面。函数组件因其简洁性成为现代React开发的首选,而Hooks的引入进一步增强了函数组件的功能,减少了对类组件的依赖。理解组件的创建、props与state的使用,以及组件间的交互,是掌握React开发的关键。
3. Props
React中的props
是属性(Properties)的简称,它是组件间传递信息的一种方式。通过props,父组件可以将数据和行为传递给子组件,使组件具有高度的可复用性和解耦性。以下是React Props的详细说明和示例:
3.1. Props 基本概念
- 只读性:Props是不可变的,子组件不能修改接收到的props,以保证数据流向的单一性和组件的纯净性。
- 传递方式:Props以JavaScript对象的形式传递,当React组件被调用时,作为参数传递给组件。
- 使用场景:用于从父组件向子组件传递数据或函数(如事件处理器)。
- 类型检查:React支持静态类型检查(如使用TypeScript或PropTypes),以确保组件使用正确的props类型。
基本示例
假设有一个简单的Welcome
组件,它接受一个name
prop来显示欢迎信息。
jsx
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// 使用组件时传递一个name属性
const element = <Welcome name="Alice" />;
ReactDOM.render(element, document.getElementById('root'));
3.2. 默认Props
可以为props指定默认值,当父组件没有提供某个prop时,会使用默认值。
jsx
Welcome.defaultProps = {
name: 'Stranger'
};
3.3. 解构Props
为了代码更加清晰,可以使用ES6的解构赋值直接从props提取所需属性。
jsx
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>;
}
3.4. PropTypes
为了验证传递给组件的props是否符合预期类型,可以使用PropTypes。
jsx
import PropTypes from 'prop-types';
function Welcome({ name }) {
// ...
}
Welcome.propTypes = {
name: PropTypes.string
};
3.5. 嵌套Props与children
除了直接定义的props外,props.children
是一个特殊属性,用于获取组件间嵌套的内容。
jsx
function Card(props) {
return (
<div className="card">
{props.header}
{props.children}
<button onClick={props.onClick}>Learn More</button>
</div>
);
}
// 使用Card组件,并在其中嵌入内容作为children
const cardElement = (
<Card header="About Us">
<p>Welcome to our company...</p>
</Card>
);
小结
React的props机制是构建可复用、模块化UI组件的关键。通过精确控制组件间的接口,开发者可以构建复杂的应用程序,同时保持组件的独立性和可测试性。理解如何有效使用props,是掌握React开发的基础。
4. State
React中的state
是组件内部的状态,它使得组件能够管理并响应自身的数据变化,进而更新UI。与props
不同,state
是可变的,并且只能在拥有它的组件内部进行修改。下面是React State的详细说明和示例。
4.1. State 基本概念
- 内部状态:State是组件的私有数据,代表组件的当前状态,比如计数器的当前值、表单的输入值等。
- 可变性:与props不同,state是可以改变的,当state发生变化时,React会自动重新渲染该组件及其子组件(如果有必要)。
- 初始化 :通常在构造函数
constructor
中初始化state,或者使用函数组件的useState
Hook。 - 修改规则 :直接修改state是不被允许的,必须通过调用
setState
方法(类组件)或更新useState
提供的setter函数(函数组件)来更新状态。
类组件中的State示例
jsx
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 }; // 初始化状态
}
incrementCount = () => {
this.setState(prevState => ({ count: prevState.count + 1 })); // 更新状态
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.incrementCount}>Increment</button>
</div>
);
}
}
函数组件中的State(Hooks)
从React 16.8开始,引入了Hooks,使得函数组件也能使用state。
jsx
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // 初始化状态
const incrementCount = () => {
setCount(count + 1); // 更新状态
};
return (
<div>
<p>Count: {count}</p>
<button onClick={incrementCount}>Increment</button>
</div>
);
}
State 的注意事项
- 避免直接修改state:直接修改state可能会导致React忽略更新或出现不可预知的行为,应始终使用setState或useState提供的setter函数。
- 状态提升:当多个组件需要共享相同的状态时,应该将状态提升到最近的共同祖先组件中。
- 使用immer思想:虽然React setState的更新是异步的,但推荐使用immer库的思想,即通过函数(在setState中或useState的setter)接收前一个状态并返回新状态,而不是直接修改旧状态。
- 性能优化 :避免不必要的state更新,可以使用
React.memo
来避免不必要的渲染(对于函数组件)。
通过上述示例和说明,可以看到React的State机制是实现动态UI和组件间数据管理的关键。正确管理和更新state,是构建高效React应用的基础。
5. 生命周期函数(部分已废弃)
在React中,组件的生命周期是指组件从创建到销毁整个过程中的不同阶段,以及在这些阶段中可以执行的特定方法(也称为生命周期方法)。这些方法允许开发者在适当的时间执行代码,如初始化数据、响应用户交互或清理资源。然而,自React 16.3版本以来,一些经典生命周期方法已被标记为不安全或废弃,并推荐使用新的Hooks API来替代。
5.1. 挂载阶段
constructor()
: 类组件实例化时调用,用于初始化state和绑定方法。static getDerivedStateFromProps()
: 组件实例被创建和更新时调用,根据props来计算并返回新的state或返回null。render()
: 必须由组件实现,用于根据当前props和state返回React元素。componentDidMount()
: 组件首次挂载到DOM后立即调用,适合进行DOM操作、数据获取等。
5.2. 更新阶段
static getDerivedStateFromProps()
: 同样在更新时调用,用于基于新的props计算state。shouldComponentUpdate(nextProps, nextState)
: 判断组件是否需要更新,返回true/false。render()
: 状态或props改变后,再次调用以反映最新变化。getSnapshotBeforeUpdate(prevProps, prevState)
: 在DOM更新前调用,可以返回一个值用作componentDidUpdate
的第三个参数。componentDidUpdate(prevProps, prevState, snapshot)
: 组件更新后立即调用,适合执行副作用,如网络请求。
5.3. 卸载阶段
componentWillUnmount()
: 组件即将卸载前调用,用于清理工作,如取消网络请求、清理定时器等。
随着React生态的发展,Hooks已经成为现代React开发的标准做法,它们提供了更灵活、易于理解和测试的组件模型。尽管经典生命周期方法在某些遗留代码中可能仍可见,但转向Hooks被视为最佳实践。
6. Hooks
React Hooks 是 React 16.8 版本引入的一个革命性特性,它允许你在函数组件中使用状态(state)和其他 React 特性,而不需要编写类组件。
React Hooks的设计理念与机制是为了简化组件的逻辑、提高代码的可读性和可维护性,同时鼓励函数式编程风格。其核心目标是将状态管理和副作用逻辑从类组件中解耦,使得这些功能可以在函数组件中直接使用。以下是React Hooks设计的主要理念、模拟生命周期的方法以及其带来的好处。
设计理念与机制
-
单一职责原则 :每个Hook专注于一个特定功能,如管理状态(
useState
)、处理副作用(useEffect
)、获取上下文(useContext
)等,这有助于保持组件的纯净性和逻辑的清晰性。 -
函数式编程:Hooks推动使用纯函数组件,减少了类的使用,使得代码更加简洁和易于推理。
-
组合而非继承:Hooks通过组合不同的功能,允许开发者以更模块化的方式构建组件,避免了类继承的复杂性。
6.1. useState
useState
是最基础的 Hook,它让你在函数组件中添加 state。它返回一对值:当前 state 和一个让你更新 state 的函数。
示例:
jsx
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
6.2. useEffect
useEffect
Hook 允许你在函数组件中执行副作用操作(side effects),比如数据获取、订阅或者手动修改DOM等。你可以认为它是 componentDidMount
, componentDidUpdate
, 和 componentWillUnmount
这三个生命周期方法的组合。
- 模拟 componentDidMount
和 componentWillUnmount
- 模拟挂载 : 当你需要在组件挂载后执行一些操作,如API调用、订阅等,可以使用
useEffect
,并提供一个空数组[]
作为依赖项列表。这样,该副作用函数只会在组件挂载后和卸载前执行一次。
jsx
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
console.log("Component did mount");
// 这里执行挂载后的操作,如API请求
return () => {
console.log("Component will unmount");
// 这里执行卸载前的清理工作,如取消API请求、清除定时器等
};
}, []); // 空依赖数组确保只在挂载和卸载时执行
// ...
}
- 模拟 componentDidUpdate
- 模拟更新 : 如果你需要在组件的props或state改变后执行一些操作,可以使用
useEffect
并提供一个依赖项数组,列出所有需要监听的变化。
jsx
import React, { useState, useEffect } from 'react';
function MyComponent({ someProp }) {
const [myState, setMyState] = useState(initialState);
useEffect(() => {
console.log("Component did update");
// 这里执行更新后的操作,注意只有在someProp或myState改变时才会执行
}, [someProp, myState]); // 依赖项数组,当这些值变化时触发执行
// ...
}
6.3. useContext
useContext
Hook 使你能够在组件树中无需显式传递 props 就能传递数据。这对于跨层级传递数据特别有用。
示例:
jsx
import React, { useContext } from 'react';
const ThemeContext = React.createContext();
function ComponentA() {
return (
<ThemeContext.Provider value="dark">
<ComponentB />
</ThemeContext.Provider>
);
}
function ComponentB() {
const theme = useContext(ThemeContext);
return <div>The theme is {theme}</div>;
}
6.4. 其他 Hooks
除了上述三个基本 Hooks 外,还有更多高级 Hooks,如 useReducer
用于管理更复杂的 state 更新逻辑,useCallback
和 useMemo
用于优化性能,useRef
用于创建可变的引用对象,还有useImperativeHandle
, useLayoutEffect
, useDebugValue
等Hooks。
React Hooks 的设计原则是让组件的逻辑更易于理解、测试和重用,减少类组件中生命周期方法的复杂性。通过组合不同的 Hooks,你可以构建出强大且灵活的功能性组件。
-
简化组件逻辑:Hooks让组件逻辑更集中,易于理解和测试,特别是对于那些涉及状态管理和副作用处理的代码。
-
更好的代码复用:由于Hooks是纯函数,它们可以在多个组件之间轻松复用,有助于减少重复代码。
-
增强函数组件能力:使得函数组件也能拥有与类组件相同的功能,如状态管理、生命周期等,促进了函数式组件的广泛采用。
-
易于调试和测试:函数组件和Hooks的简洁性降低了调试难度,同时提升了单元测试的便利性。
通过这些设计理念和机制,React Hooks显著提高了开发效率,使得React应用的构建和维护变得更加高效和愉快。
6.5. 注意事项
-
条件执行 : 类组件中可能会在生命周期方法内使用条件判断来决定是否执行某些操作,而在函数组件中,你同样可以在
useEffect
的回调函数内部进行条件判断。 -
避免无限循环 : 当在
useEffect
中修改会导致该effect重新执行的state时,要确保正确管理依赖项数组,避免造成无限循环。
结论
随着React生态的发展,Hooks已经成为现代React开发的标准做法,它们提供了更灵活、易于理解和测试的组件模型。尽管经典生命周期方法在某些遗留代码中可能仍可见,但转向Hooks被视为最佳实践。
这些核心概念构成了React应用开发的基石,熟练掌握它们是进行高效React开发的前提。
React基于组件化思想,采用虚拟DOM技术,通过对比差异最小化实际DOM操作,实现高效页面更新。其生命周期管理机制确保组件状态与UI同步。Hooks的引入,进一步提升函数组件能力,简化状态逻辑。React重视声明式编程,提升代码可读性和可维护性,对现代Web开发的效率与体验至关重要。