文章目录
持续补充中…
简介
由Facebook开源的,用于动态构建用户界面的 JavaScript 库(只关注于视图)。具有如下特点:
1.声明式编码
2.组件化编码
3.React Native 编写原生应用
4.高效(优秀的Diffing算法)
React高效的原因:
1.使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。
2.DOM Diffing算法, 最小化页面重绘。
- 英文官网: https://reactjs.org/
- 中文官网: https://react.docschina.org/
虚拟DOM
创建虚拟DOM
// 第一种方式:纯JS
React.createElement(component, props, ...children)
//example:
const VDOM = React.createElement('h1',{id:'title'},'<span>this is a virtal children dom</span>')
// 第二种方式:JSX
const VDOM = <h1 id='title'><span>this is a virtal children dom</span></h1>
渲染虚拟DOM
ReactDOM.render(virtualDOM, containerDOM)
// example:
ReactDOM.render(VDOM, document.getElementById('app'))
React脚手架
npx create-react-app [project name]
脚手架初始化的目录结构:
public ---- 静态资源文件夹
favicon.icon ------ 网站页签图标
index.html -------- 主页面
logo192.png ------- logo图
logo512.png ------- logo图
manifest.json ----- 应用加壳的配置文件
robots.txt -------- 爬虫协议文件
src ---- 源码文件夹
App.css -------- App组件的样式
App.js --------- App组件
App.test.js ---- 用于给App做测试
index.css ------ 样式
index.js ------- 入口文件
logo.svg ------- logo图
reportWebVitals.js --- 页面性能分析文件(需要web-vitals库的支持)
setupTests.js ---- 组件单元测试的文件(需要jest-dom库的支持)
函数式组件
函数组件又称为无状态组件,不能使用state
,只可以使用只读的props
:
function App(props) {
return (
<>
hello react
</>
);
}
Hooks:
Hook是React 16.8.0版本增加的新特性/新语法,可以让我们在函数组件中使用 state 以及其他的 React 特性。
React.useState()
:
State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作:
// React.useState()语法:
// 参数: 第一次初始化指定的值在内部作缓存
// 返回值: 包含2个元素的数组, 第1个为State内部当前状态值, 第2个为更新状态值的函数
const [xxx, setXxx] = React.useState(initValue)
// setXxx()有2种写法:
setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
React.useEffect()
:
Effect Hook 可以让我们在函数组件中执行副作用操作(模拟类组件中的生命周期钩子):
useEffect(() => {
// 在此可以执行任何带副作用操作
console.log('do something here')
return () => { // 清除函数会在组件卸载前执行。另外,如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除。
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
console.log('do something here')
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行;如果为state,则在state值更新后执行。
React.useRef()
:
Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据:
// 语法:
const refContainer = useRef()
// 作用:保存标签对象,功能与React.createRef()一样
React.useContext()
:
祖孙组件之间传值:
React.userReducer()
:
userReducer可以配合useContext实现类似redux的功能:
自定义Hook:
React Hook只能在组件或者Hook中使用,Hook命名得以 use
开头,例如如下实现的两个自定义Hook:
// 自定义一个隐藏[]的专门用于初始化的hook
export default useMounted = (callback) => {
useEffect(() => {
callback()
}, [])
}
// 自定义一个防抖动的hook,如受控组件双向绑定值后,在input框中快速输入内容时,不让值每次改变时都马上执行useEffect方法,而是停顿一定时间后才执行
export default useDebounce = (value, delay) => {
const [debounceValue, setDebounceValue] = useState(value)
useEffect(() => {
// 每次在value变化后,设置一个定时器
const timeout = setTimeout(() => setDebounceValue(value), delay)
// 新的useEffect销毁上一个useEffect设置的定时器,使上一次值改变不生效
return () => clearTimeout(timeout)
}, [value])
}
类式组件
组件3大核心属性
props:
state:
ref:
React路由
setState
1.对象式的setState:
// 1.stateChange为状态改变对象(该对象可以体现出状态的更改)
// 2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
setState(stateChange, [callback])
// example:
2.函数式的setState:
// 1.updater为返回stateChange对象的函数; updater可以接收到state和props。
// 2.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
setState(updater, [callback])
// example:
总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ,则使用对象方式
;
(2).如果新状态依赖于原状态,则使用函数方式
;
(3).如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中读取。
生命周期
生命周期的三个阶段:
-
初始化阶段: 由
ReactDOM.render()
触发—初次渲染1.
constructor()
2.getDerivedStateFromProps
3.render()
4.componentDidMount()
-
更新阶段: 由组件内部
this.setSate()
或父组件重新render
触发1.
getDerivedStateFromProps
2.shouldComponentUpdate()
3.render()
4.getSnapshotBeforeUpdate
5.componentDidUpdate()
-
卸载组件: 由
ReactDOM.unmountComponentAtNode()
触发componentWillUnmount()
常用和重要的钩子:
// 初始化渲染或更新渲染调用
render()
//开启监听, 发送ajax请求
componentDidMount()
//做一些收尾工作, 如: 清理定时器
componentWillUnmount()
React UI组件库 Antd
yarn add antd
组件使用和主题自定义参考官方文档:https://ant.design/index-cn
LazyLoad
//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
const Login = lazy(()=>import('@/pages/Login'))
//2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
<Suspense fallback={<h1>loading.....</h1>}>
<Switch>
<Route path="/xxx" component={Xxxx}/>
<Redirect to="/login"/>
</Switch>
</Suspense>
Fragment
作用:可以不用必须有一个真实的DOM根标签了
// 第一种方式:跟标签有属性值时使用
<Fragment><Fragment>
// 第二种方式:无属性值时使用,更简便
<></>
Context
React中一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信。
//1) 创建Context容器对象:
const XxxContext = React.createContext()
//2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
<xxxContext.Provider value={父数据}>
子组件
</xxxContext.Provider>
//3) 后代组件读取数据:
//第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
//第二种方式: 函数组件与类组件都可以
<xxxContext.Consumer>
{
value => ( // value就是context中的value数据
要显示的内容
)
}
</xxxContext.Consumer>
在应用开发中一般不用context, 一般都用它封装react插件。
Redux
组件通信方式总结
组件间的关系:
- 父子组件
- 兄弟组件(非嵌套组件)
- 祖孙组件(跨级组件)
几种通信方式:
- props:children props 和 render props
- 消息订阅-发布:pubs-sub、event等等
- 集中式管理:redux、dva等等
- conText:生产者 - 消费者模式
推荐的搭配方式:
- 父子组件:props
- 兄弟组件:消息订阅-发布、集中式管理
- 祖孙组件(跨级组件):消息订阅 - 发布、集中式管理、conText(开发用的少,封装插件用的多)
Component优化
Component的2个问题:
只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
效率高的做法:
只有当组件的state或props数据发生改变时才重新render()
原因:
Component声明周期中的
shouldComponentUpdate()
方法总是返回true
解决方法1:
重写
shouldComponentUpdate()
方法,比较新旧state
或props
数据, 如果有变化才返回true
, 如果没有返回false
解决方法2(推荐):
使用
PureComponent
。PureComponent
重写了shouldComponentUpdate()
, 只有state
或props
数据有变化才返回true
注意:
- 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
- 不要直接修改state数据, 而是要产生新数据
Error boundary(错误边界)
Error boundary 用来捕获后代组件错误,渲染出备用页面。但是只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
使用方式:getDerivedStateFromError
配合 componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}
使用自定义配置文件
项目根目录下,即和src
同级下创建.env
和.env.development
两个文件,分别表示生产环境和开发环境下的不同配置:
// 配置文件内容
// .env
REACT_APP_TEST = hahaha
// .env.development
REACT_APP_TEST = lalala
配置文件中变量的命名规则为 REACT_APP_
+ NAME
,如上述的:REACT_APP_TEST
。
读取配置文件和使用变量:
import React from 'react';
const Test = process.env.REACT_APP_TEST
export default function App() {
return (
<>
hello react
{console.log(process.env.NODE_ENV)}
{console.log(Test )}
</>
)
}
// 控制台输出为:development 和 lalala
CSS IN JS
React默认集成了 module.js
,只需将原.css
后缀的样式文件改为.module.css
即可,通过 import
样式文件作为一个对象,然后调用对象属性的方式给 className
赋值实现样式添加:
/* index.module.css */
.container {
height: 100vh;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.box {
width: 400px;
}
.title {
text-align: center;
font-size: 2rem;
color: #636363;
}
.logo {
text-align: center;
margin-bottom: 5px;
}
.divider :global(.ant-divider-plain.ant-divider-with-text) {
font-size: 12px;
color: gray;
}
// login.js
<div className={style.logo}>
<Avatar size={48}>ICU</Avatar>
</div>
<div className={style.title}>统一登录认证平台</div>
<div className={style.divider}>
<Divider orientation="center" plain>其他登录方式</Divider>
</div>
上述实现了自定义的class
和覆盖了antd
的原生样式,覆盖使用:global
,覆盖样式为了不污染全局,所以再套一个父级。