React
React 官方文档 React 官方中文文档 React getting started React 官方入门教程 react 快速入门
声明式 React 使创建交互式 UI 变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React 能高效更新并渲染合适的组件。
以声明式编写 UI,可以让你的代码更加可靠,且方便调试。
组件化 构建管理自身状态的封装组件,然后对其组合以构成复杂的 UI。 由于组件逻辑使用 JavaScript 编写而非模板,因此你可以轻松地在应用中传递数据,并保持状态与 DOM 分离。
一次学习,跨平台编写 无论你现在使用什么技术栈,在无需重写现有代码的前提下,通过引入 React 来开发新功能。 React 还可以使用 Node 进行服务器渲染,或使用 React Native 开发原生移动应用。
React 思想
-
虚拟DOM(Virtual DOM) React将DOM抽象为虚拟DOM,虚拟DOM其实就是用一个对象来描述DOM,通过对比前后两个对象的差异,最终只把变化的部分重新渲染,提高渲染的效率 为什么用虚拟dom,当dom反生更改时需要遍历 而原生dom可遍历属性多大231个 且大部分与渲染无关 更新页面代价太大
-
Diff算法(虚拟DOM的加速器,提升React性能的法宝) 当你使用React的时候,在某个时间点 render() 函数创建了一棵React元素树, 在下一个state或者props更新的时候,render() 函数将创建一棵新的React元素树, React将对比这两棵树的不同之处,计算出如何高效的更新UI(只更新变化的地方)
React 的核心概念
JSX
为什么要用 JSX? 你不需要为了 React 使用 JSX,可以直接使用原生 JS。但是,我们建议使用 JSX 是因为它能精确,也是常用的定义包含属性的树状结构的语法。
它对于非专职开发者比如设计师也比较熟悉。 XML 有固定的标签开启和闭合的优点。这能让复杂的树更易于阅读,优于方法调用和对象字面量的形式。 它没有修改 JavaScript 语义。
组件
React 中拥有多种不同类型的组件
函数组件
定义组件最简单的方式就是编写 JavaScript 函数:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}class 组件
你同时还可以使用 ES6 的 class 来定义组件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}上述两个组件在 React 里是等效的。
生命周期方法
挂载
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
- constructor()
- static getDerivedStateFromProps()
- render()
- componentDidMount() UNSAFE_componentWillMount()//即将弃用
更新
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate() UNSAFE_componentWillUpdate()//即将弃用 UNSAFE_componentWillReceiveProps()//即将弃用
卸载
当组件从 DOM 中移除时会调用如下方法:
- componentWillUnmount()
错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
- static getDerivedStateFromError()
- componentDidCatch()
其他
组件还提供了一些额外的 API: setState() forceUpdate()
class 属性: defaultProps displayName
实例属性: props state
state 和 props 状态
React 的核心机制是能够在数据发生变化的时候自动重新渲染 UI,那么势必要有一个让我们保存状态的地方,这个保存状态的机制就是 state。 而 props 就是类似于 Html 标记上属性的概念,是为了在父子组件之间传递状态。
in short props: 基本同Vue一样; state: 是可变的, 类同 Vue2 data() 选项 和Vue3的reactive,ref 响应式数据
props
为了在父子组件之间传递状态
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
// 这段代码会在页面上渲染 “Hello, Sara”:
<Welcome name="Sara" />;State
一个有状态的组件
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(state => ({
seconds: state.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div>
Seconds: {this.state.seconds}
</div>
);
}
}
root.render(<Timer />);State 是私有的,并且完全受控于当前组件。 Class 组件应该始终使用 props 参数来调用父类的构造函数。
- 不要直接修改 State 例如,此代码不会重新渲染组件:
// Wrong
this.state.comment = 'Hello';而是应该使用 setState():
// Correct
this.setState({comment: 'Hello'});- 异步更新 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。 例如,此代码可能会无法更新计数器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});事件处理
React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
使用 React 时,你一般不需要使用 addEventListener 为已创建的 DOM 元素添加监听器。事实上,你只需要在该元素初始渲染的时候添加监听器即可。
当你使用 ES6 class 语法定义一个组件的时候,通常的做法是将事件处理函数声明为 class 中的方法。例如,下面的 Toggle 组件会渲染一个让用户切换开关状态的按钮:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);如果觉得使用 bind 很麻烦,这里有两种方式可以解决。如果你正在使用实验性的 public class fields 语法,你可以使用 class fields 正确的绑定回调函数:
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
....
}如果你没有使用 class fields 语法,你可以在回调中使用箭头函数:
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}渲染
- 与运算符 &&
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}- 三目运算符
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}列表渲染
可以变量是个JSX
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
//注意列表渲染, 最好要给key属性
<li key={number.toString()}>
{number}
</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);JSX 允许在大括号中嵌入任何表达式,所以我们可以内联 map() 返回的结果:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}阻止组件渲染
在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染。
表单
在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。例如这个纯 HTML 表单只接受一个名称:
<form>
<label>
名字:
<input type="text" name="name" />
</label>
<input type="submit" value="提交" />
</form>此表单具有默认的 HTML 表单行为,即在用户提交表单后浏览到新页面。如果你在 React 中执行相同的代码,它依然有效。但大多数情况下,使用 JavaScript 函数可以很方便的处理表单的提交, 同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用 “受控组件”。
受控组件
在 HTML 中,表单元素(如<input>、 <textarea> 和 <select>)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。
我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('提交的名字: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}in short input,textarea之类的有自己stat的元素; 1. 使用 value属性绑定React的stat,2. 再监听onChange事件修改React 的stat 从而实现’唯一’绑定
比vue麻烦… 官方推荐 Formik?
组件 包含
in short 类似vue的插槽的概念
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
// 这个属性, 名称可以随便取, 新增.
// children 等于 vue 默认插槽
{props.children}
</div>
);
}Hook
in short 类似vue的组合式API, useState可用来给函数组件添加State; useEffect可用来给函数组件添加生命周期回调
useState Hook
有时候我们会想要在组件之间重用一些状态逻辑。目前为止,有两种主流方案来解决这个问题:高阶组件和 render props。
import React, { useState } from 'react';
function Example() {
// 声明一个叫 “count” 的 state 变量。
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}在这里,useState 就是一个 Hook。通过在函数组件里调用它来给组件添加一些内部 state。React 会在重复渲染时保留这个 state。
useState 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。\它类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并。
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}使用 State Hook useState 主要用来给函数组件, 添加stat, 还可以在组件之间重用一些状态逻辑
Effect Hook
当你调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数。 由于副作用函数是在组件内声明的,所以它们可以访问到组件的 props 和 state。 默认情况下,React 会在每次渲染后调用副作用函数 —— 包括第一次渲染的时候。
// 相当于 componentDidMount 和 componentDidUpdate:
useEffect(() => {
// 使用浏览器的 API 更新页面标题
document.title = `You clicked ${count} times`;
});
副作用函数还可以通过返回一个函数来指定如何“清除”副作用。
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
//React 会在组件销毁时调用
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。或者在自定义 Hook中也可以
in short 可以用来给函数组件, 添加生命周期回调
自定义 Hook
自定义 Hook 解决了以前在 React 组件中无法灵活共享逻辑的问题。 自定义 Hook
自定义 Hook 必须以 “use” 开头 自定义 Hook 是一种重用状态逻辑的机制,所以每次使用自定义 Hook 时,其中的所有 state 和副作用都是完全隔离的。 每次调用 Hook,它都会获取独立的 state。
import React, { useEffect, useState } from 'react'
/**
* 自定义hook,做一个轮询后台的处理,每隔1s钟发一次请求
*/
export function useTimerReqHooks() {
// 计时器记录的数据 // 时间变化请求
const [count, setCount] = useState<number>(0);
const [data, setData] = useState<number[]>([]);
// 计数器, 实例
let timer: number | null = null;
// 副作用, 发起请求
useEffect(() => {
timer = setInterval(() => {
setCount(pre => pre + 1);
(async () => {//包起来使 await生效
const res = await getData(1, 10);//Ajax 异步请求数据
setData(pre => [...pre, res]);//参考上: State 异步更新
})()
}, 1000)
return () => {
// 清除 副作用
if (timer) { clearInterval(timer); timer = null; }
}
}, [count]);
return {
data,
count
}
}
//组件
function ListComp() {
const { count, data } = useTimerReqHooks();
const liDom = data.map((it, index) => (<li key={index}>{it}</li>));
return (<>
<p>次数: {count}</p>
<p>数据</p>
<ul> {liDom} </ul>
</>)
}Hello Word
npx create-react-app hello-react
Umi 框架
Umi,中文发音为「乌米」,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。
然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。
关键特性: 技术收敛, 开发者依赖 Umi 之后就无需关心 babel、webpack、postcss、react、react-router 等依赖,而依赖 @umijs/max 之后无需再关心开发中台项目的依赖和技术栈。 ProComponents 是基于 Ant Design 而开发的模板组件,提供了更高级别的抽象支持,开箱即用。可以显著的提升制作 CRUD 页面的效率,更加专注于页面。