本文介绍了 React 的演变历程,从传统的 HTML + JS + CSS 开发方式到现代的 JSX 语法糖。详细讲解了如何通过 create-react-app 创建 React 工程,以及 React 的核心概念如组件、状态管理和属性传递。通过实战代码示例,帮助你快速掌握 React 的基本能力和开发流程,提升前端开发效率。
-
从传统前端开发到 React JSX 的演变
在 学习 react 之前先来分析一下 react 出现的历史背景。
在传统开发方式下, 前端是如何构建页面的。
在传统方式下, 前端页面是由 HTML + 原生 JS + CSS 构成。
- JS 的本质就是一个浏览器脚本, 可以操作 DOM, 可以去增删改查 DOM元素和属性。
- JS 是一种可以操作 DOM 的动态语言。
那么我们可不可以完全不写 HTML, 只通过 JS 来构建 DOM呢? 答案是可以的。
基于 JS 创建 DOM
有了这个想法, 那么怎么来实现呢? 下面通过伪代码来设计一下实现思路。
我期望我的的任意一个标签都可以通过 JS 函数来创建,封装一个方法来做这个事情。
createElement
function createElement(type,params,...children){
let el
// 根据 type 创建 dom 元素
// 根据 params挂载元素属性, 根据不同的特征设置, 比如 setAttribute、addEventlistener 等等
// 遍历 children 挂载元素
return el
}
按照这样的逻辑
创建下面的 HTML 就可以用 JS 来实现
<div id="app">
<h1></h1>
<h2></h2>
<h3></h3>
</div>
createElement("div",{id:"app"}
createElement("h1"),
createElement("h2"),
createElement("h3"),
)
React 最早就是用这种形式创建 DOM, 但是太难用了。所以希望用按照 HTML 的形式来写, 但是还是 JS 的东西。
于是 React 去找了 Babel 团队, 写了 @babel/preset-react 编译器。于是将 JSX 可以转换成 createElement。
React.createElement 与上面的有什么区别?
本质上都是 JSX, 经历了 Babel 编译器进行编译出来 React.CreateElement。
JSX 是 CreateElement 的语法糖, 通过 Babel 编译成 JS 代码。
React 本身不支持 jsx, jsx 只是语法糖方便我们编写, 最终还是借助相关的 Babel preset 把 jsx 语法转换为 React.createElement(...) JS引擎才可以识别, Vue 也可以写 JSX, 借助相关的 babel 转换为 Vue 中的 Render函数。
前端在做什么?
Vue 简单交互流程
React 简单交互流程
React 灵活性
React JSX 的灵活性在于它将 HTML 结构和 JavaScript 逻辑无缝结合,使得开发者可以在同一语法中动态生成内容和管理状态,极大地提升了代码的可读性和可维护性。
{ arr.map(item=> <div>{item.xxx}</div>)}
-
创建 React 工程
在构建现代前端应用时,React 是一个强大的工具,它提供了组件化的开发方式和高效的更新机制。创建一个 React 工程是开始使用 React 开发应用的第一步。
使用 CRA 创建 React App
create-react-app 可以通过 npx(npm 5.2.0 及以上版本附带的工具)来创建新的 React 应用。npx 会自动下载并运行 create-react-app,而不需要全局安装它。
如果你更喜欢全局安装 create-react-app,可以使用以下命令:
npm install -g create-react-app
create-react-app my-app
npx create-react-app demo-app
npx create-react-app demo-app-ts --template typescript
pnpm run eject
# pnpm run eject # 会将代码怎么打包、启动的脚本都暴露出来。
-
React 的基本能力
每一个 React 文件都是一个组件, 有组件就有子父组件。
-
子父组件
组件又有函数组件和类组件。
webstorm 创建模板小技巧: rsf 函数组件、rcc类组件
-
从界面的视角看
- class 组件渲染的是 render 函数中的 return 的内容。
- function 组件渲染的是函数本身返回的内容。
-
State
在 react 中有一个概念叫 state
-
如果我有一个数据, 我需要数据改变的时候,去触发界面更新。
- 我要把这个数据定义为 state
- 使用特定的方法去更新 state
-
类组件的 State
setState: 类组件要使用 setState 方法来管理状态。
- state 的值互相不影响
- 第二个参数是一个 callback, 能拿到更新后的 state
import React, { Component } from "react";
class ClassCom extends Component {
constructor(props) {
super(props);
this.state = {
number: 0,
msg: "hello react",
};
}
handleClick = (type) => {
this.setState(
{
number: this.state.number + (type === "plus" ? 1 : -1),
},
() => {
console.log(`number in callback: `, this.state.number);
},
);
// 取不到最新的值,需要在 setState 的 cb 中获取
// 原因 setState 有一个 isBatchedUpdate 要等所有任务执行完毕后再更新。
console.log(`number in handleClick: `, this.state.number);
};
handleChange = (e) => {
this.setState({
msg: e.target.value,
});
};
render() {
const { number, msg } = this.state;
return (
<div>
<h2>类组件 state 的用法</h2>
<h3>{msg}</h3>
<input type="text" value={msg} onChange={this.handleChange} />
<p>this number is {number}</p>
<button onClick={this.handleClick.bind(this, "plus")}>
点击增加 number
</button>
<button onClick={() => this.handleClick("minus")}>
点击减少 number
</button>
</div>
);
}
}
export default ClassCom;
类组件 setState 用法总结
- constructor中使用 this.state = {} 声明状态
- 通过 this.setState 修改状态的值两个参数,一个设置值,一个回调函数用来获取更新后的值
- setState 有一个 isBatchedUpdate 要等所有任务执行完毕后再更新。
- input 数据双向绑定: value + onChange
- 注意 class 中的 this, 解决方案两种 bind 和 箭头函数。
-
函数组件的 State
useState Hook
函数签名 [state, dispatch] = useState(initState)
-
state 作为组件的状态, 提供给 UI 渲染视图。
-
dispatch 用户修改 state 的方法, 同时触发组件更新。
- dispatch的参数可以是函数,可以不是。如果是函数就更新为函数执行的结果,否则直接更新为值。
-
initState 初始值, 可以是函数也可以是值。
import React, { useState } from 'react';
function Counter() {
// 初始化 state 为 0
const [count, setCount] = useState(0);
return (
<div>
<p>你点击了 {count} 次</p>
<button onClick={() => setCount(count + 1)}>点击增加</button>
</div>
);
}
export default Counter;
-
Props
子父组件传值的工具 类组件: props 传递
函数组件: props 传递
: props 传递import ClassCom from "./src/basic/ClassCom";
import FuncCom from "./src/basic/FuncCom";
import { useState } from "react";
function App() {
const [count, setCount] = useState(1024);
const handleClick = () => {
setCount((number) => ++number);
};
return (
<div className="App">
<ClassCom name={`我是类组件`} />
<FuncCom name={`我是函数组件`} count={count} onClick={handleClick}>
<h1>Hello Slot</h1>
</FuncCom>
</div>
);
}
export default App;
import React, { Component } from "react";
class ClassCom extends Component {
constructor(props) {
super(props);
this.state = {
number: 0,
msg: "hello react",
};
}
handleClick = (type) => {
this.setState(
{
number: this.state.number + (type === "plus" ? 1 : -1),
},
() => {
console.log(`number in callback: `, this.state.number);
},
);
// 取不到最新的值,需要在 setState 的 cb 中获取
// 原因 setState 有一个 isBatchedUpdate 要等所有任务执行完毕后再更新。
console.log(`number in handleClick: `, this.state.number);
};
handleChange = (e) => {
this.setState({
msg: e.target.value,
});
};
render() {
const { number, msg } = this.state;
return (
<div>
<h2>{this.props.name}类组件 state 的用法</h2>
<h3>{msg}</h3>
<input type="text" value={msg} onChange={this.handleChange} />
<p>this number is {number}</p>
<button onClick={this.handleClick.bind(this, "plus")}>
点击增加 number
</button>
<button onClick={() => this.handleClick("minus")}>
点击减少 number
</button>
</div>
);
}
}
export default ClassCom;
import React, { useState } from "react";
function FuncCom({ name, count, onClick, children }) {
const [number, setNumber] = useState(0);
const [msg, setMsg] = useState("hello react");
const handleChange = (event) => {
setMsg(event.target.value);
};
const handleClick = (type) => {
//setNumber(type === "plus" ? number + 1 : number - 1);
//setNumber((prevState) => (type === "plus" ? prevState + 1 : prevState - 1));
setNumber((number) => number + 1);
console.log(number);
};
return (
<div>
<p>name:{name}</p>
<p>
count:{count} <button onClick={onClick}>修改count</button>
</p>
<h2>函数组件 state 的用法</h2>
<p>{msg}</p>
{ /*非受控组件*/ }
<input type="text" />
{ /*受控组件: 数据和state一一绑定 */ }
<input type="text" value={msg} onChange={handleChange} />
<p> this is number: {number}</p>
<button onClick={() => handleClick("plus")}>plus number</button>
<button onClick={() => handleClick("minus")}>minus number</button>
{children}
</div>
);
}
function SubCom(props) {
return <div>SubCom</div>;
}
export default FuncCom;
-
条件渲染和列表渲染
import React, { useState } from "react";
function RenderCom(props) {
const [list, setList] = useState(["javascript", "ecmascript", "typescript"]);
const [flag, setFlag] = useState(false);
return (
<div>
{list.map((item) => (
<div key={item.name}>{item.name}</div>
))}
{flag ? <div>Hello</div> : <div>Bye Bye</div>}
</div>
);
}
export default RenderCom;
总结
- React 的基本原理思想: 从 createElement 到 JSX
- 脚手架创建 React 工程的方法
- React 的基本能力: JSX 的用法、子父组件引用、State 的用法、Props 组件传递状态
本文深入探讨了 React 的演变和基本能力,从传统的 HTML + JS + CSS 开发方式,到使用 React 和 JSX 的现代化开发流程。首先,我们了解了如何通过 JavaScript 操作 DOM,并通过伪代码设计了 createElement 函数来动态创建 DOM 元素。接着,本文介绍了 React 的核心概念,包括组件、状态管理(state)、属性传递(props)以及条件和列表渲染。我们还展示了如何使用 create-react-app 快速创建和配置 React 项目。通过这些内容,你可以清晰地理解 React 的基本原理和实用技巧,掌握如何高效地构建和管理前端应用。