React 笔记

React 笔记
强烈推介IDEA2020.2破解激活,IntelliJ IDEA 注册码,2020.2 IDEA 激活码

持续补充中…

简介

由Facebook开源的,用于动态构建用户界面的 JavaScript 库(只关注于视图)。具有如下特点:
 
1.声明式编码
2.组件化编码
3.React Native 编写原生应用
4.高效(优秀的Diffing算法)

React高效的原因:

1.使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。
2.DOM Diffing算法, 最小化页面重绘。

虚拟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函数中读取。

生命周期

在这里插入图片描述
生命周期的三个阶段:

  1. 初始化阶段: 由ReactDOM.render()触发—初次渲染

    1.constructor()
    2.getDerivedStateFromProps
    3.render()
    4.componentDidMount()

  2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发

    1.getDerivedStateFromProps
    2.shouldComponentUpdate()
    3.render()
    4.getSnapshotBeforeUpdate
    5.componentDidUpdate()

  3. 卸载组件: 由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个问题:

  1. 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低

  2. 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

效率高的做法:

只有当组件的state或props数据发生改变时才重新render()

原因:

Component声明周期中的shouldComponentUpdate()方法总是返回true

解决方法1:

重写shouldComponentUpdate()方法,比较新旧stateprops数据, 如果有变化才返回true, 如果没有返回false

解决方法2(推荐):

使用PureComponentPureComponent重写了shouldComponentUpdate(), 只有stateprops数据有变化才返回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,覆盖样式为了不污染全局,所以再套一个父级。

本文来源MrKorbin,由架构君转载发布,观点不代表Java架构师必看的立场,转载请标明来源出处:https://javajgs.com/archives/25230

发表评论