react 合成事件
由于react中存在一个根标签,所以在react中给元素所绑定的事件,其实并不是直接绑定在元素本身,而是通过事件合成绑定在了这个根标签上,通过冒泡事件触发,每当有事件注册时,react内部就会创建一个映射表,将事件和触发事件的元素进行绑定,当触发事件后,根标签捕捉到事件,通过映射表给对应的元素委派事件,在最新版本的react中,捕获阶段时合成事件要先于原生事件执行,在冒泡阶段,原生事件先执行,有的时候我们的事件会执行两次就是因为存在这个事件合成的机制。
react 虚拟dom
虚拟DOM是一个特殊的JS对象,它描述的是真实的dom结构,类似于dom树,但他不是真实的DOM,所以叫做虚拟DOM,虚拟DOM的好处是可以通过一些特殊的算法,用来减少对真实DOM的操作次数,提高性能。
谈谈你对react fiber 结构理解
Fiber 是现在react中核心的算法,它的出现是为了解决以前js引擎和渲染引擎互斥,一个执行另一个会被挂起等待,有的时候会由于js引擎算法过于复杂导致页面加载时间过长,它可以把一个渲染任务分解为多个渲染任务,而不是一次性完成,把每一个分割得很细的任务视作一个"执行单元",React 就会检查现在还剩多少时间,如果没有时间就将控制权让出去,故任务会被分散到多个帧里面,中间可以返回至主进程控制执行其他任务,最终实现更流畅的用户体验。
key是什么
key是react用来追踪哪些列表的元素被修改,被添加或者是被删除的辅助标示。在开发过程中我们需要保证某个元素的key在其同级元素中具有唯一性。
如果列表数据渲染中,在数据后面插入一条数据,key作用并不大,前面的元素在diff算法中,前面的元素由于是完全相同的,并不会产生删除创建操作,在最后一个比较的时候,则需要插入到新的DOM树中
key作用
注意事项为:
1).父传子props
父组件通过props的方式向子组件传递数据,但是props是单向绑定,只能是父组件向子组件传递
2).子传父
在父组件中定义回调函数,加在子组件的属性上,子组件调用该方法给父组件传递数据
3).非嵌套组件通信Context和props 逐层传递
useParams 作用:获取params参数,返回,参数独享
search参数
useSearchParams()作用:获取seach参数,返回值:数组
0: 参数对象, .get(key) 获取具体参数
1: setSearch() 更改search 参数
React 自己实现了这么一套事件机制,它在 DOM 事件体系基础上做了改进,减少了内存的消耗,并且最大程度上解决了 IE 等浏览器的不兼容问题
那它有什么特点?
document
这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)event.stopPropagation()
无效的原因。SyntheticEvent
,不是原生的,这个可以自己去看官网类式组件的功能更为完整,它存在函数式组件不存在的一些生命周期钩子,比如在组件挂在前执行的钩子,在组件更新前执行的钩子,还存在错误边界钩子,当有这些特殊需求时,只能选择使用类式组件,
但在平常开发中推荐使用函数式组件,因为函数式组件没有this,所以不需要自己手动绑定this,操作更为简便,再函数式组件有了hook之后功能基本上和类式组件没有差别,而且函数式组件更好拆分,更适合模块化的开发
路由存在history模式和hash模式
React是什么:
用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案
遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效
使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流
帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面
react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容
React特性:
优势:
虚拟DOM(Virtual DOM)
真实DOM(Real DOM)
虚拟dom原理流程
用JavaScript模拟DOM树,并渲染这个DOM树
比较新老DOM树,得到比较的差异对象
把差异对象应用到渲染的DOM树
JSX是什么:
JSX就是Javascript和XML结合的一种格式。是 JavaScript 的语法扩展,只要你把HTML代码写在JS里,那就是JSX。
在实际开发中,JSX 在产品打包阶段都已经编译成纯 JavaScript,不会带来任何副作用,反而会让代码更加直观并易于维护。官方定义是:类 XML 语法的 ECMAScript 扩展
特点:
props是组件对外的接口,用于组件之间的传值,props在组件内部是不可修改,可以看作只读属性。
state可以看作组件的私有属性,用于组件内部的数据传递。
state可在组件内进行修改,state初始化的地方是在constructor()构建函数中,组件内修改必须使用setState()函数。
说到setState()不得不再啰嗦几句。react的面试面试官经常会问setState()是同步还是异步?既然这么问了那当然是异步啦。
setState是异步更新,同步执行,因为react中会将多个setState合并在一起处理,从而提升程序的性能,这就是react高明的其中一点。
所以在编写程序时我们不能依赖于当前的state去计算下一个state。因为异步所以this.state不一定是最新的组件属性值。
对于以上问题是否有解决办法?那肯定有呀。
既然是异步,那肯定就有回调函数,说到回调函数相比大家都知道解决办法啦。
那setState()函数的回调函数在哪呢,这个大家应该都知道,但是我就是想写。
setState()函数有两个参数,第一个我就不赘述了,第二个就是它的回调啦。
refs是什么:
Refs 在计算机中称为弹性文件系统(英语:Resilient File System,简称ReFS)
React 中的 Refs提供了一种方式,允许我们访问 DOM节点或在 render方法中创建的 React元素
本质为ReactDOM.render()返回的组件实例,如果是渲染组件则返回的是组件实例,如果渲染dom则返回的是具体的dom节点
创建ref的形式:
传入字符串,使用时通过 this.refs.传入的字符串的格式获取对应的元素
传入对象,对象是通过 React.createRef() 方式创建出来,使用时获取到创建的对象中存在 current 属性就是对应的元素
传入函数,该函数会在 DOM 被挂载时进行回调,这个函数会传入一个 元素对象,可以自己保存,使用时,直接拿到之前保存的元素对象即可
传入hook,hook是通过 useRef() 方式创建,使用时通过生成hook对象的 current 属性就是对应的元素
应用场景:
在某些情况下,我们会通过使用refs来更新组件,但这种方式并不推荐,更多情况我们是通过props与state的方式进行去重新渲染子元素过多使用refs,会使组件的实例或者是DOM结构暴露,违反组件封装的原则例如,避免在 Dialog 组件里暴露 open() 和 close() 方法,最好传递 isOpen 属性但下面的场景使用refs非常有用:
对Dom元素的焦点控制、内容选择、控制
对Dom元素的内容设置及媒体播放
对Dom元素的操作和对组件实例的操作
集成第三方 DOM 库
在ES6中,通过super
关键字实现调用父类,super
代替的是父类的构建函数;
在React中,类组件是基于es6的规范实现的,继承React.Component,因此如果用到constructor就必须写super()才初始化this,这时候,在调用super()的时候,我们一般都需要传入props作为参数,如果不传进去,React内部也会将其定义在组件实例中
// React 内部
const instance = new YourComponent(props);
instance.props = props;
React 有一套自己的事件系统,其事件叫做合成事件。
React 事件概述
React 根据W3C 规范来定义自己的事件系统,其事件被称之为合成事件 (SyntheticEvent)。而其自定义事件系统的动机主要包含以下几个方面:
(1)抹平不同浏览器之间的兼容性差异。最主要的动机。
(2)事件"合成",即事件自定义。事件合成既可以处理兼容性问题,也可以用来自定义事件(例如 React 的 onChange 事件)。
(3)提供一个抽象跨平台事件机制。类似 VirtualDOM 抽象了跨平台的渲染方式,合成事件(SyntheticEvent)提供一个抽象的跨平台事件机制。
(4)可以做更多优化。例如利用事件委托机制,几乎所有事件的触发都代理到了 document,而不是 DOM 节点本身,简化了 DOM 事件处理逻辑,减少了内存开销。(React 自身模拟了一套事件冒泡的机制)
(5)可以干预事件的分发。V16引入 Fiber 架构,React 可以通过干预事件的分发以优化用户的交互体验。
注:「几乎」所有事件都代理到了 document,说明有例外,比如、标签的一些媒体事件(如 onplay、onpause 等),是 document 所不具有,这些事件只能够在这些标签上进行事件进行代理,但依旧用统一的入口分发函数(dispatchEvent)进行绑定。
this
的值将会是undefined
。4种方式绑定this
在构造函数中使用bind绑定this
在调用的时候使用bind绑定this
在调用的时候使用箭头函数绑定this
在声明方法的时候使用箭头方法绑定this
几种方式的区别
总结:建议使用xxx = () => {}定义方法,这样性能最佳,使用也相对方便;
React的生命周期分为两种情况,一种是16.3版本前的,还有一种是16.3版本后的,本着早写晚写都要写的原则,
第一种生命周期:
Initialization:初始化阶段**
Mounting:挂载阶段
Updation:更新阶段
Unmounting:卸载阶段
第二种生命周期
相比于16.3版本之前的生命周期,新的生命周期将所有的will的生命周期都删除了个干净,此外新加了getDerviedStateFromProps和getSnapshotBeforeUpdate。
挂载阶段:
更新阶段:
卸载阶段:
错误处理:
getDerviedtateFromError:从错误中获取state
componentDidCatch:捕获错误并进行处理
React
中的组件根据是否受React
控制可分为受控的(controlled)和非受控的(uncontrolled)。
多数情况下,推荐使用受控组件实现表单。在受控组件中,表单数据由组件控制。
另外一种是非受控组件,这种方式下表单组件由DOM自身控制。
受控组件
受控组件通过props
获取其当前值,并通过回调函数(比如onChange
)通知变化
表单状态发生变化时,都会通知React
,将状态交给React
进行处理,比如可以使用useState
存储
受控组件中,组件渲染出的状态与它的value
或checked
属性相对应
受控组件会更新state
的流程
非受控组件
非受控组件将数据存储在DOM
中,而不是组件内,这比较类似于传统的HTML
表单元素。
state
和props
控制ref
从DOM
中获取元素数据React 基于 Virtual DOM 实现了一个 SyntheticEvent 层(合成事件层), 定义的事件处理器会接收到一个合成事件对象的实例, 它符合 W3C 标准, 且与原生的浏览器事件拥有同样的接口, 支持冒泡机制, 所有的事件都自动绑定在最外层上。
在 React 底层, 主要对合成事件做了两件事:
事件委派: React 会把所有的事件绑定到结构的最外层, 使用统一的事件监听器, 这个事件监听器上维持了一个映射来保存所有组件内部事件监听和处理函数
自动绑定: React 组件中, 每个方法的上下文都会指向该组件的实例, 即自动绑定 this 为当前组件
虚拟dom(virtual dom) 其实就是一个JavaScript对象,通过这个JavaScript对象来描述真实dom。
真实dom:
以前没有虚拟dom,如果需要比较两个页面的差异,我们需要通过对真实dom进行比对。真实dom节点是非常复杂的,它里面会绑定的事件,它会有属性,背后会有各种方法,会频繁触发重排与重绘,所以两个真实dom比对,非常耗性能。 总损耗 = 真实DOM完全增删改 + (可能较多的节点)重排与重绘
虚拟dom:相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提髙性能。
总损耗 = 虚拟DOM增删改 + (与Diff算法效率有关)真实DOM差异增删改 + (较少的节点)重排与重绘
具体实现步骤如下:
用JavaScript对象结构表示DOM树的结构;然后用这个树构建一个真正的DOM树,插到文档当中;
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;
把步骤2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。
高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。基本上,这是从React的组成性质派生的一种模式,我们称它们为 “纯”组件, 因为它们可以接受任何动态提供的子组件,但它们不会修改或复制其输入组件的任何行为。
一、、是什么
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
至于为什么引入hook,官方给出的动机是解决长时间使用和维护react过程中常遇到的问题,例如:
难以重用和共享组件中的与状态相关的逻辑
逻辑复杂的组件难以开发与维护,当我们的组件需要处理多个互不相关的 local state 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面
类组件中的this增加学习成本,类组件在基于现有工具的优化上存在些许问题
由于业务变动,函数组件不得不改为类组件等等
在以前,函数组件也被称为无状态的组件,只负责渲染的一些工作
因此,现在的函数组件也可以是有状态的组件,内部也可以维护自身的状态以及做一些逻辑方面的处理
二、有哪些
上面讲到,Hooks让我们的函数组件拥有了类组件的特性,例如组件内的状态、生命周期
最常见的hooks有如下:
useState
useEffect
其他
常见的CSS引入方式有以下:
区别:
在react
中,react-transition-group
是一种很好的解决方案,其为元素添加enter
,enter-active
,exit
,exit-active
这一系列勾子,可以帮助我们方便的实现组件的入场和离场动画
其主要提供了三个主要的组件:
Redux-thunk 是 redux 的中间件。
它主要设计用来处理项目中的异步操作,如获取接口数据等。
过去异步操作都会写在组件中,请求到数据再创建 action。这使得异步操作遍布很多组件文件中,维护不便,且不易于自动化测试。
Redux-thunk 主要理念是:将所有异步操作都放在 action 里面去处理,与业务组件代码解耦。而要实现这个理念,需要 action 处理异步逻辑,需要它是一个函数。
实现逻辑:
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
使用方法:
react是用于搭建页面的,解决渲染DOM的过程,但是整个页面中会有很多组件,每个组件的state都是自身管理,这样写着没有问题,但是在开发和后期维护阶段就显得比较繁琐,而redux可以对所有的状态进行集中管理,这样需要更新状态的时候,只需要对这个管理集中处理就行
工作原理:
把Ui组件比作买奶茶的,store就是收银员,action就是订单,Reducer 就是后面做饮料的小伙子,state就是你的饮料
首先ui组件去买饮料会先对store说自己想喝什么,store就会拿着订单active给后面做饮料的小伙子reducer,做完后reducer会把饮料给收银员,输入state
转换为代码是,Ui组件 需要获取一些数据, 然后它就告知 Store 需要获取数据,这就是就是 Action , Store 接收到之后让 Reducer 弄一下, Reducer 就会知道 Store 想要什么数据
redux就是一个实现上述集中管理的容器,遵循三大基本原则:
单一事实来源:
整个应用的状态存储在单个 store 中的对象/状态树里。单一状态树可以更容易地跟踪随时间的变化,并调试或检查应用程序。
状态是只读的:
改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示。
使用纯函数进行更改:
为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于其参数值的函数
react-redux,我们便不再需要使用store的subscribe自己去订阅状态了。UI组件就像普通组件一样内部没有redux的身影。可读性更高。
react-redux将组件分为UI组件和容器组件,UI组件只负责UI的呈现,不带有任何业务逻辑,通过props接收数据,不使用Rdux的API,一般保存在components文件夹下,容器组件,只负责管理数据和业务逻辑,不负责UI的呈现,使用redux的API,一般保存在containers文件夹下。
使用redux的redux-thunk插件来处理
createStore里的第二个参数放入applyMiddleware(thunk)这样就可以解决异步
import {createStore,applyMiddleware,combineReducers} from 'redux'
import thunk from 'redux-thunk'
import countReducer from './count_reducer'
// combineReducers 用来访问多个请求
let routReducer = combineReducers({
count:countReducer,
})
export default createStore(routReducer,applyMiddleware(thunk))
首先我们要知道什么的react-redux
redux是用于数据状态管理,而react是一个视图层面的库,如果将两者连接在一起,可以使用官方推荐react-redux库,其具有高效且灵活的特性
react-redux将组件分成:
容器组件:存在逻辑处理
UI 组件:只负责现显示和交互,内部不处理逻辑,状态由外部控制
通过redux将整个应用状态存储到store中,组件可以派发dispatch行为action给store
其他组件通过订阅store中的状态state来更新自身的视图
如何做,使用方法
使用react-redux分成了两大核心:
Provider 在redux中存在一个store用于存储state,如果将这个store存放在顶层元素中,其他组件都被包裹在顶层元素之上
connection 将store
上的getState
和 dispatch
包装成组件的props
// 创建容器组件,建立容器组件与Ui组件的联系
import { connect } from "react-redux";
connect(
state => ({ // mapStateToProps方法 把redux中的数据映射到react中的props中去
count: state,
}),
dispatch => ({ // mapDispatchToProps方法 将redux中的dispatch映射到组件内部的props中
onClick:data=>dispatch(createAddCount(data)),
})
)(Count)
可以根据项目具体情况进行选择,以下列出两种常见的组织结构
按角色组织(MVC)
按功能组织
使用redux
使用功能组织项目,也就是把完成同一应用功能的代码放在一个目录下,一个应用功能包含多个角色的代码
Redux
中,不同的角色就是reducer
、actions
和视图,而应用功能对应的就是用户界面的交互模块
每个功能模块对应一个目录,每个目录下包含同样的角色文件:
actionTypes.js 定义action类型
actions.js 定义action构造函数
reducer.js 定义这个功能模块如果响应actions.js定义的动作
views 包含功能模块中所有的React组件,包括展示组件和容器组件
index.js 把所有的角色导入,统一导出
由于router和switch对于路由的渲染策略不同,对router来说,如果有的链接既可以被路由A匹配,又可以被路由B匹配,那么Router会同时渲染它们
对于switch来说,它只会渲染符合条件的第一个路径,避免重复匹配