前言
1 组件通讯
1.1 props
子组件
1 | import React from "react"; |
父组件
1 | <EightteenChildOne name={'props 传入的 name 值'} eightteenChildOneToFather={(mode)=>this.eightteenChildOneToFather(mode)}></EightteenChildOne> |
props 传多个值时:
传统写法
1 | const {dataOne,dataTwo,dataThree} = this.state |
升级写法
1 | <Com {...{dataOne,dataTwo,dataThree}}> |
1.2 props 升级版
原理:子组件里面利用 props 获取父组件方法直接调用,从而改变父组件的值
注意: 此方法和 props 大同小异,都是 props 的应用,所以在源码中没有举例
调用父组件方法改变该值
1 | // 父组件 |
1.3 Provider,Consumer和Context
1.Context在 16.x 之前是定义一个全局的对象,类似 vue 的 eventBus,如果组件要使用到该值直接通过this.context获取
1 | //根组件 |
2.16.x 之后的Context使用了Provider和Customer模式,在顶层的Provider中传入value,在子孙级的Consumer中获取该值,并且能够传递函数,用来修改context
声明一个全局的 context 定义,context.js
1 | import React from 'react' |
父组件导入
1 | // 导入 Provider |
子组件
1 | // 导入Consumer |
1.4 EventEmitter
EventEmiter 传送门
使用 events 插件定义一个全局的事件机制
1.5 路由传参
1.params
1 | <Route path='/path/:name' component={Path}/> |
2.query
1 | <Route path='/query' component={Query}/> |
3.state
1 | <Route path='/sort ' component={Sort}/> |
4.search
1 | <Route path='/web/search ' component={Search}/> |
这个在 react-router-dom: v4.2.2有 bug,传参跳转页面会空白,刷新才会加载出来
5.优缺点
- 1.params在HashRouter和BrowserRouter路由中刷新页面参数都不会丢失
- 2.state在BrowserRouter中刷新页面参数不会丢失,在HashRouter路由中刷新页面会丢失
- 3.query:在HashRouter和BrowserRouter路由中刷新页面参数都会丢失
- 4.query和 state 可以传对象
1.6 onRef
原理:onRef 通讯原理就是通过 props 的事件机制将组件的 this(组件实例)当做参数传到父组件,父组件就可以操作子组件的 state 和方法
EightteenChildFour.jsx
1 | export default class EightteenChildFour extends React.Component { |
eighteen.jsx
1 | <EightteenChildFour onRef={this.eightteenChildFourRef}></EightteenChildFour> |
1.7 ref
原理:就是通过 React 的 ref 属性获取到整个子组件实例,再进行操作
EightteenChildFive.jsx
1 | // 常用的组件定义方法 |
eighteen.jsx
1 | // 钩子获取实例 |
1.8 redux
redux 是一个独立的事件通讯插件,这里就不做过多的叙述
请戳传送门
1.9 MobX
MobX 也是一个独立的事件通讯插件,这里就不做过多的叙述
请戳传送门:
1.10 flux
flux 也是一个独立的事件通讯插件,这里就不做过多的叙述
请戳传送门:
1.11 hooks
1.hooks 是利用 userReducer 和 context 实现通讯,下面模拟实现一个简单的 redux
2.核心文件分为 action,reducer,types
action.js
1 | import * as Types from './types'; |
reducer.js
1 | import * as Types from "./types"; |
types.js
1 | export const EXAMPLE_TEST = 'EXAMPLE_TEST'; |
eightteen.jsx
1 | export const ExampleContext = React.createContext(null);//创建createContext上下文 |
EightteenChildThree.jsx // 组件
1 | import React, { useEffect, useContext } from 'react'; |
3.hooks其实就是对原有React 的 API 进行了封装,暴露比较方便使用的钩子;
4.钩子有:
钩子名 | 作用 |
---|---|
useState | 初始化和设置状态 |
useEffect | componentDidMount,componentDidUpdate和componentWillUnmount和结合体,所以可以监听useState定义值的变化 |
useContext | 定义一个全局的对象,类似 context |
useReducer | 可以增强函数提供类似 Redux 的功能 |
useCallback | 记忆作用,共有两个参数,第一个参数为一个匿名函数,就是我们想要创建的函数体。第二参数为一个数组,里面的每一项是用来判断是否需要重新创建函数体的变量,如果传入的变量值保持不变,返回记忆结果。如果任何一项改变,则返回新的结果 |
useMemo | 作用和传入参数与 useCallback 一致,useCallback返回函数,useMemo 返回值 |
useRef | 获取 ref 属性对应的 dom |
useImperativeMethods | 自定义使用ref时公开给父组件的实例值 |
useMutationEffect | 作用与useEffect相同,但在更新兄弟组件之前,它在React执行其DOM改变的同一阶段同步触发 |
useLayoutEffect | 作用与useEffect相同,但在所有DOM改变后同步触发 |
5.useImperativeMethods
1 | function FancyInput(props, ref) { |
1.12 slot
slot 就是将父组件的标签传给子组件,类似vue 的 v-slot
场景:有些组件只是共用部分dom 逻辑,里面有部分逻辑是独立的
1 | // 父组件文件 |
1.13 对比
方法 | 优点 | 缺点 |
---|---|---|
props | 不需要引入外部插件 | 兄弟组件通讯需要建立共同父级组件,麻烦 |
props 升级版 | 不需要引入外部插件,子传父,不需要在父组件用方法接收 | 同 props |
Provider,Consumer和Context | 不需要引入外部插件,跨多级组件或者兄弟组件通讯利器 | 状态数据状态追踪麻烦 |
EventEmitter | 可支持兄弟,父子组件通讯 | 要引入外部插件 |
路由传参 | 可支持兄弟组件传值,页面简单数据传递非常方便 | 父子组件通讯无能为力 |
onRef | 可以在获取整个子组件实例,使用简单 | 兄弟组件通讯麻烦,官方不建议使用 |
ref | 同 onRef | 同 onRef |
redux | 建立了全局的状态管理器,兄弟父子通讯都可解决 | 引入了外部插件 |
mobx | 建立了全局的状态管理器,兄弟父子通讯都可解决 | 引入了外部插件 |
flux | 建立了全局的状态管理器,兄弟父子通讯都可解决 | 引入了外部插件 |
hooks | 16.x 新的属性,可支持兄弟,父子组件通讯 | 需要结合 context 一起使用 |
slot | 支持父向子传标签 |
redux , mobx和flux对比
方法 | 介绍 |
---|---|
redux | 1.核心模块:Action,Reducer,Store;2. Store 和更改逻辑是分开的;3. 只有一个 Store;4. 带有分层 reducer 的单一 Store;5. 没有调度器的概念;6. 容器组件是有联系的;7. 状态是不可改变的;8.更多的是遵循函数式编程思想 |
mobx | 1.核心模块:Action,Reducer,Derivation;2.有多个 store;3.设计更多偏向于面向对象编程和响应式编程,通常将状态包装成可观察对象,一旦状态对象变更,就能自动获得更新 |
flux | 1.核心模块:Store,ReduceStore,Container;2.有多个 store; |
2.require.context()
这个是 webpack 的 api,这个在 vue 技巧中有介绍,因为 Vue 和 React 工程都是基于 webpack打包,所以在 react 也可以使用
1 | const path = require('path') |
3.Decorator
定义:decorator是ES7的一个新特性,可以修改class的属性
1 | import React from 'react' |
decorators.js
1 | function testable(target) { |
很多中间件,像 redux 里面就封装了Decorator的使用
4.使用 if…else
场景:有些时候需要根据不同状态值页面显示不同内容
1 | import React from "react"; |
5.state 值改变的五种方式
方式 1
1 | let {count} = this.state |
方式 2:callBack
1 | this.setState(({count})=>({count:count+2})) |
方式 3:接收 state 和 props 参数
1 | this.setState((state, props) => { |
方式 4:hooks
1 | const [count, setCount] = useState(0) |
方式 5:state 值改变后调用
1 | this.setState( |
6.监听states 变化
1.16.x 之前使用componentWillReceiveProps
1 | componentWillReceiveProps (nextProps){ |
注意:有些时候componentWillReceiveProps在 props 值未变化也会触发,因为在生命周期的第一次render后不会被调用,但是会在之后的每次render中被调用 = 当父组件再次传送props
2.16.x 之后使用getDerivedStateFromProps,16.x 以后componentWillReveiveProps也未移除
1 | export default class Six extends React.Component { |
7.组件定义
方式 1:ES5 的Function 定义
1 | function FunCom(props){ |
方式 2: ES5的 createClass 定义
1 | const CreateClassCom = React.createClass({ |
方式 3:ES6 的 extends
1 | class Com extends React.Component { |
调用
1 | export default class Seven extends React.Component { |
区别: ES5的 createClass是利用function模拟class的写法做出来的es6;
通过es6新增class的属性创建的组件此组件创建简单.
8.通过 ref 属性获取 component
方式 1:也是最早的用法,通过 this.refs[属性名获取]
也可以作用到组件上,从而拿到组件实例
1 | class RefOne extends React.Component{ |
方式 2:回调函数,在dom节点或组件上挂载函数,函数的入参是dom节点或组件实例,达到的效果与字符串形式是一样的,都是获取其引用
1 | class RefTwo extends React.Component{ |
方式 3:React.createRef()
React 16.3版本后,使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,该ref的current属性,将能拿到dom节点或组件的实例
1 | class RefThree extends React.Component{ |
方式 4:React.forwardRef
React 16.3版本后提供的,可以用来创建子组件,以传递ref
1 | class RefFour extends React.Component{ |
子组件通过React.forwardRef来创建,可以将ref传递到内部的节点或组件,进而实现跨层级的引用。forwardRef在高阶组件中可以获取到原始组件的实例.这个功能在技巧 18 会着重讲
9.static 使用
场景:声明静态方法的关键字,静态方法是指即使没有组件实例也可以直接调用
1 | export default class Nine extends React.Component { |
注意:
- 1.ES6的class,我们定义一个组件的时候通常是定义了一个类,而static则是创建了一个属于这个类的属性或者方法
- 2.组件则是这个类的一个实例,component的props和state是属于这个实例的,所以实例还未创建
- 3.所以static并不是react定义的,而加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,所以也是无法访问到 this
- 4.getDerivedStateFromProps也是通过静态方法监听值,详情请见技巧 6
10.constructor和super
回顾:
- 1.谈这两个属性之前,先回顾一下ES6 函数定义方法
- 2.每一个使用class方式定义的类默认都有一个constructor函数, 这个函数是构造函数的主函数, 该函数体内部的this指向生成的实例
- 3.super关键字用于访问和调用一个对象的父对象上的函数
1 | export default class Ten extends React.Component { |
11.PropTypes
场景:检测传入子组件的数据类型
类型检查PropTypes自React v15.5起已弃用,请使用prop-types
方式 1:旧的写法
1 | class PropTypeOne extends React.Component { |
方法 2:利用 ES7 的静态属性关键字 static
1 | class PropTypeTwo extends React.Component { |
12.使用类字段声明语法
场景:可以在不使用构造函数的情况下初始化本地状态,并通过使用箭头函数声明类方法,而无需额外对它们进行绑定
1 | class Counter extends Component { |
13.异步组件
1.场景:路由切换,如果同步加载多个页面路由会导致缓慢
2.核心 API:
- loader:需要加载的组件
- loading:未加载出来的页面展示组件
- delay:延迟加载时间
- timeout:超时时间
3.使用方法:
安装 react-loadable ,babel插件安装 syntax-dynamic-import. react-loadable是通过webpack的异步import实现的
1 | const Loading = () => { |
4.Loadable.Map()
并行加载多个资源的高阶组件
14.动态组件
场景:做一个 tab 切换时就会涉及到组件动态加载
实质上是利用三元表达式判断组件是否显示
1 | class FourteenChildOne extends React.Component { |
如果是单个组件是否显示可以用短路运算
1 | oneShowFlag&&<FourteenChildOne></FourteenChildOne> |
15.递归组件
场景:tree组件
利用React.Fragment或者 div 包裹循环
1 | class Item extends React.Component { |
16.受控组件和不受控组件
受控组件:组件的状态通过React 的状态值 state 或者 props 控制
1 | class Controll extends React.Component { |
不受控组件:组件不被 React的状态值控制,通过 dom 的特性或者React 的ref 来控制
1 | class NoControll extends React.Component { |
导入代码:
1 | export default class Sixteen extends React.Component { |
17.高阶组件(HOC)
17.1 定义
1.就是类似高阶函数的定义,将组件作为参数或者返回一个组件的组件;
2.作用:
- 抽取重复代码,实现组件复用,常见场景,页面复用;
- 条件渲染,控制组件的渲染逻辑(渲染劫持),常见场景,权限控制;
- 捕获/劫持被处理组件的生命周期,常见场景,组件渲染性能追踪、日志打点
17.2 实现方法
1.属性代理
1 | import React,{Component} from 'react'; |
2.反向继承
原理就是利用 super 改变改组件的 this 方向,继而就可以在该组件处理容器组件的一些值
1 | const Seventeen = (WrappedComponent)=>{ |
18.元素是否显示
一般用三元表达式
1 | flag?<div>显示内容</div>:'' |
19.Dialog 组件创建
Dialog 应该是用的比较多的组件,下面有三种不同的创建方法
方式 1:通过 state 控制组件是否显示
1 | class NineteenChildOne extends React.Component { |
方式 2:通过ReactDom.render创建弹层-挂载根节点外层
通过原生的createElement,appendChild, removeChild和react 的ReactDOM.render,ReactDOM.unmountComponentAtNode来控制元素的显示和隐藏
NineteenChild.jsx
1 | import ReactDOM from "react-dom"; |
nineteen.jsx
1 | twoSubmit=()=>{ |
20.React.memo
作用:当类组件的输入属性相同时,可以使用 pureComponent 或 shouldComponentUpdate 来避免组件的渲染。现在,你可以通过把函数组件包装在 React.memo 中来实现相同的功能
1 | import React from "react"; |
21.React.PureComponent
作用:
- 1.React.PureComponent 和 React.Component类似,都是定义一个组件类。
- 2.不同是React.Component没有实现shouldComponentUpdate(),而 React.PureComponent通过props和state的浅比较实现了。
- 3.React.PureComponent是作用在类中,而React.memo是作用在函数中。
- 4.如果组件的props和state相同时,render的内容也一致,那么就可以使用React.PureComponent了,这样可以提高组件的性能
1 | class TwentyOneChild extends React.PureComponent{ //组件直接继承React.PureComponent |
22.React.Component
作用:是基于ES6 class的React组件,React允许定义一个class或者function作为组件,那么定义一个组件类,就需要继承React.Component
1 | export default class TwentyTwo extends React.Component{ //组件定义方法 |
23.在 JSX 打印 falsy 值
定义:
1.falsy 值 (虚值) 是在 Boolean 上下文中认定为 false 的值;
2.值有 0,””,’’,``,null,undefined,NaN
1 | export default class TwentyThree extends React.Component{ |
虚值如果直接展示,会发生隐式转换,为 false,所以页面不显示
24.ReactDOM.createPortal
作用:组件的render函数返回的元素会被挂载在它的父级组件上,createPortal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案
1 | import React from "react"; |
这样元素就追加到指定的元素下面啦
25.在 React 使用innerHTML
场景:有些后台返回是 html 格式字段,就需要用到 innerHTML 属性
1 | export default class TwentyFive extends React.Component { |
26.React.createElement
语法:
React.createElement(type,[props],[…children])
源码:
1 | export default class TwentySix extends React.Component { |
原理:实质上 JSX 的 dom 最后转化为 js 都是React.createElement
// jsx 语法
1 | <div id='one' class='two'> |
// 转化为 js
1 | React.createElement( |
27.React.cloneElement
语法:
1 | React.cloneElement( |
作用:这个方法的作用是复制组件,给组件传值或者添加属性
核心代码
1 | React.Children.map(children, child => { |
28.React.Fragment
作用:React.Fragment可以让你聚合一个子元素列表,并且不在DOM中增加额外节点
核心代码
1 | render() { |
29.循环元素
内部没有封装像 vue 里面 v-for 的指令,而是通过 map 遍历
1 | {arr.map((item,index)=>{ |
30.给 DOM 设置和获取自定义属性
作用:有些要通过自定义属性传值
1 | export default class Thirty extends React.Component { |
31.绑定事件
场景:交互就会涉及到事件点击,然后点击选中值传参也是一个很常见场景
1 | import React from "react"; |
32.React-Router
32.1 V3和 V4的区别
- 1.V3或者说V早期版本是把router 和 layout components 分开;
- 2.V4是集中式 router,通过 Route 嵌套,实现 Layout 和 page 嵌套,Layout 和 page 组件 是作为 router 的一部分;
- 3.在V3 中的 routing 规则是 exclusive,意思就是最终只获取一个 route;
- 4.V4 中的 routes 默认是 inclusive 的,这就意味着多个; 可以同时匹配和呈现.如果只想匹配一个路由,可以使用Switch,在 中只有一个 会被渲染,同时可以再在每个路由添加exact,做到精准匹配
Redirect,浏览器重定向,当多有都不匹配的时候,进行匹配
32.2 使用
1 | import { HashRouter as Router, Switch } from "react-router-dom"; |
V4是通过 Route 嵌套,实现 Layout 和 page 嵌套,Switch切换路由的作用
33.样式引入方法
方式 1:import 导入
1 |
方式 2:内联方式
1 | import React from 'react'; |
34.动态绑定 className
原理:通过三元表达式控制 className 值
1 | render(){ |