import CmdTab from "../../components/PackageManagerTabs";
React
面经:-)React
是用于构建用户界面的 JavaScript
库,React
核心只关注视图,不断优化算法,改进性能,提高开发和交互体验。
React
迭代稳定,重视兼容和过渡,在国内外,尤其是南方,都有相当多的公司在使用 React
。
渐进式的思想同样表现在 React
的学习曲线上,能够与传统的 Web
技术共存,灵活的 JSX
语法等都会让 React
上手很快, 而庞大生态赋予了 React
更强能力的同时,也让开发者感叹花费了更多时间在社区里遨游。
React
应用React
在前端开发领域应用广泛,使用 React
可以构建 Web
,插件,单页应用,App
,小程序,桌面端,服务端等,微服务,Serverless
,低代码,虚拟现实等都有 React
的用武之地。
React
面试注意事项React
面试题可以分为以下 4 个方面
基础:ES5+
作用域,class
,箭头函数,this 指向,异步编程,高阶函数的循环等常问
会用:state
,副作用,Hook
,加载渲染过程,路由、测试、调试、TS
、Redux
等常问
原理:Virtual DOM
,Diff
算法,设计组件,优化性能,原理和实现等高级岗位、大厂常问
项目:项目结构,技术栈,工具链,解决问题,担任角色,亮点等常问,也是面试问题来源
新手最好先熟练 ES5+ 的使用,再上手 React
时,可以边阅读边写代码,适当练习若干项目,再看原来生涩的表述,会有亲切的画面感。带着项目去看面试题,联想练习或工作中遇到的实际问题,加上自己的理解,你的回答一定可以比手册总结得更自然,更能得到面试官的肯定。
React
和 Vue支持 Virtual DOM
支持响应式和组件化的视图组件
核心库、路由和状态管理分离
支持 JSX
,移动端都支持原生渲染
预编译
React
可以通过 Prepack
优化 JavaScript
源代码,在编译时执行原本在运行时的计算过程,通过简单的赋值序列提高 JavaScript
代码的执行效率,消除中间计算过程及分配对象操作。缓存 JavaScript
解析结果,优化效果最佳Vue
可以静态分析 template
,构造 AST
树,通过 PatchFlags
标记节点变化类型渲染
React
通过 shouldComponentUpdate
/ setState
,使用 PureCompoent
等对比前后状态和属性,手动决定是否渲染来优化Vue
推荐模板语法,自动追踪组件依赖,精确渲染状态改变的组件事件处理
React
React 17
前,事件委托到 document
,之后委托到 根节点this
需要手动绑定或使用箭头函数声明Vue
this
自动绑定执行上下文React
React
是用于构建用户界面的 JavaScript
库
声明式编写 UI
,代码可靠,便于调试
组件化开发,组件逻辑使用 JavaScript
编写而非模板,遵循单向数据流和数据绑定,状态与 DOM
分离
一次学习,随处编写,使用 Virtual DOM
,支持 浏览器、Node
服务器等多种渲染方式 和 React Native
开发原生应用
React
和Angular
核心功能
React
核心库只提供构建 UI 组件的方法,其他功能通过社区提供Angular
集成了 路由、异步请求、表单、模块化 CSS
等功能组件
React
组件推荐使用 JSX
,可以一个文件包含 HTML
、CSS
和 JS
,也可以分开Angular
组件 HTML
、CSS
和 TS
分别是一个文件DOM
React
基于Virtual DOM
,组件会被编译成 JS
对象,数据更改时通过 Diff 算法更新Angular
基于 Incremental DOM
,组件会被编译成指令,数据更改时就地更新。没有使用规定指令的组件可以被 Tree Shaking
数据绑定
React
单向数据绑定,声明状态,更新视图Angular
双向数据绑定,数据改变,更新视图全局状态管理
Angualr
可以用 Service
依赖注入实现React
可以用全局对象或 Redux
实现上手成本
React
推荐了解 JSX
,是库,可以渐进式使用Angluar
需要了解 TypeScript
,Rxjs
,OOP
和装饰器等,是框架,推荐独立使用React
的缺点是什么React
核心是 UI 库,路由,状态管理等由社区维护。细粒度需求和问题依赖社区解决
React
概念和约束较少,容易上手并与现有项目整合。代码风格和项目结构容易产生差别
React
JSX
灵活性高,预编译时可以做的优化相对其他 HTML
分离的库有限
React
setState
提供基于队列异步更新,手动优化渲染流程,需要关注业务之外的逻辑
React17
以前基于事件委托的合成事件,表现和使用与原生事件存在差异
lambda
演算,函数可以作为入参和出参MVC
和 MVVM 的区别是Model
和视图 View
MVC
Model
- 视图 View
- 控制器 Controller
MVC
和 被动 MVC
MVC
:视图订阅数据更新MVC
:控制器操作视图HTML
,利于 SEO
DOM
MVVM
Model
- 视图 View
- 视图模型 ViewModel
ViewModel
单向或双向数据绑定 View
和 Model
层,实现自动同步SSR
利于 SEO
DOM
React
项目文件结构?React
建议
项目目录嵌套最多3到4个层级
不要过度思考
没有官方推荐的组织方式,常见组织方式包括
React
18 都有哪些新特性?新的 Root API:ReactDom.creatRoot
React 17
及之前版本
ReactDom.render
将应用渲染到页面的根元素React 18
ReactDom.creatRoot
将应用渲染到页面的根元素ReactDom.flushSync
退出批量更新SSR 支持 React.lazy
和 React.Suspense
React
17 及之前版本
React.lazy()
和 React.Suspense
尚未在 ReactDOMServer
中支持React
18
React
.lazy() 和 React.Suspense
startTransition
React
17 及以前版本
所有更新都被紧急渲染
使用 setTimeout 和 防抖等方式,避免频繁更新
React
18
所有渲染分为紧急和非紧急
非紧急渲染使用 startTransition
包裹
非紧急渲染的延迟时间由设备决定
非紧急渲染可中断,不会影响响应用户输入、动画等紧急渲染
JSX
JSX
?JSX
是 JavaScript
的语法扩展,生成 React
元素
JSX
是 React.createElement(component, props, ...children)
函数的语法糖
React
17 RC 开始,由编辑器自动引入 import { jsx } from 'react/jsx-runtime'
JSX
支持 HTML
模板语法 和 表达式,支持条件和循环渲染,支持点语法和展开运算符
JSX
转义所有输入内容,防止注入攻击
JSX
忽略渲染 false,null,undefined,true
子元素
React
中使用JSX
?React
认为渲染逻辑本质上与其他 UI 逻辑内在耦合
React
将标记和逻辑共同存放在组件,实现关注点分离
React
不强制要求使用 JSX
,但 JSX
与 UI 一起有视觉辅助作用
React
通过 JSX
可以显示更多有用错误和警告消息
JSX
可以有效降低 XSS 风险?React
DOM 在渲染所有输入内容前,默认会将它们转义成字符串,有效降低 XSS 风险
可以通过dangerouslySetInnerHTML = {{ __html: HTML }}
来显示转义前的内容
JSX
中条件渲染?JSX
中循环控制?JSX
JSX
中class
变成了class
Name ?JSX
语法上更接近 JavaScript
而不是 HTML
JSX
的 class
Name 更接近 HTML DOM 对象的属性,并且支持属性拓展运算符
JSX
通过匹配闭合标签提升可读性,而不是代替 HTML
JSX
与 HTML 需要转换,直接使用 class
也无法避免其它转换工作React
组件?React
组件允许用户将 UI 拆分成独立可复用的代码片段,并对每个片段进行独立构思
React
组件从概念上类似于 JavaScript
函数
React
组件接受任意的入参 Props,返回用于描述页面展示内容的 React
元素
React
组件分成哪几类?按定义分类
class
定义,维护 state,有生命周期按状态分
按定位分
按 React
内置类型分类
ClassComponent
,由 class
创建ContextProvider
,由 createContext
创建IndeterminateComponent
,FunctionCompoent
挂载前的初始类型FunctionComponent
,即函数组件ForwardRef
,由 React.forwardRef
创建,接收 ref
并转发给子组件MemoComponent
,由 React.memo
创建,条件渲染子组件SimpleMemoCompoent
,由 React.memo
创建且不指定条件FiberNode
HostRoot
,由 ReactDOM.render
创建HostPortal
,由 React.createPortal
创建,多用于模态框HostComponent
,对应元素节点HostText
,对应文本节点Fragment
,分组子列表,无需向 DOM
添加额外节点,可用短语法<>
Profiler
,测量 React
应用多久渲染一次以及渲染一次的“代价”StrictMode
,严格模式,用来突出显示应用程序中潜在问题的工具Suspense
,等待目标代码加载,并且可以指定一个加载界面,在用户等待时显示PureCompoent
,浅层对比 prop
和 state
实现了 shouldComponentUpdate
说明 | 类组件 | 函数组件 |
---|---|---|
回调钩子 | 生命周期 | useEffect / useLayoutEffect |
this | 有,事件处理函数需绑定 this | 无 |
state | 有,this.setState 更新 | 无,useState / useReducer 引入 |
实例化 | 是 | 否 |
性能 | 现代浏览器中,闭包和类的原始性能只有在极端场景才会有明显差别 | 使用 Hooks 某些情况更加高效,避免了 class 需要的额外成本,如创建类实例和在构造函数绑定事件处理器的成本,符合语言习惯的代码不需要很深的组件库嵌套 |
受控组件
React
的 state 是表单元素的“唯一数据源”,控制用户输入过程中表单发生的操作React
组件包裹<input type="file" />
的 value 由用户设置非受控组件
React
组件包裹React
和 非 React
代码,不推荐使用高阶组件是参数为组件,返回值为新组件的函数,某种角度上就是高阶函数
高阶组件是 React
中复用组件逻辑的一种高级技巧
高阶组件不是 React
API 的一部分,它是一种基于 React
的组合特性而形成的设计模式
React.PureComponent
与 React.Component
相似,区别是
React.Component
并未实现 shouldComponentUpdate
React.PureComponent
以浅层对比 prop
和 state
方式实现了 shouldComponentUpdate
React.PureComponent
无法检查对象的深层差别prop
和 state
使用深层数据结构时
forceUpdate()
来确保组件正确更新immutable
对象 加速嵌套数据的比较React
组件按照用途可以分为展示组件和容器组件
React
推荐所有新组件,无论是展示组件,还是容器组件,都采用函数组件 + Hook 方式编写
展示组件
容器组件
React Redux
的 connect()
和 Relay
的 createFragmentContainer
React
分离展示组件和容器组件的优势
this.props.children
传递组件本身,减少相同 props
层层传递React
组件提高组件复用度?劫持 React
组件又被称为渲染劫持
将已有组件包装,注入新属性和功能,输出高阶组件,来实现组件复用
劫持需要遵守高阶组件的约定
不要改变原始组件,仅组合组件
保持组件的接口与已有组件相似,透传与自身无关的 props 给已有组件
最大化可组合性,确保函数签名类型一致,输入函数,返回函数,输入组件,返回组件
包装显示名称便于调试,如 withSubscription(CommentList)
React
组件?根据数据源和原型和 UI 稿,了解数据结构和 UI 视图
划分组件层级
构建静态版本
props
父组件到子组件单向传递UI state
最小且完整表示
props
传递来的数据state
或 props
计算得出的数据state
放置位置
state
渲染的所有组件state
应该放置在共同上级组件或者更高层级的组件中state
只能由拥有它们的组件更改state
的回调函数props
传递给子组件,在子组件中,如事件处理函数中调用React
组件与Web Components
共存的最佳实践是?访问 Web Components
的命令式 API
:使用 ref
与 DOM
节点进行交互
引入第三方 Web Components
:编写 React
组件包装该 Web Components
Web Components
触发事件:React
组件中手动添加事件处理器来处理事件
React
的状态?React
的状态 state
是一个对象
this.state
创建,通过 this.setState
合并更改,异步更新React Hook
中,状态通过 this.useState 或 this.useReducer 使用React
将组件看做状态机,状态改变触发渲染
React
建议减少有状态的组件,提高组件复用度,利于维护
React
的状态管理无法满足需求时使用 ReduxReact
的状态提升?React
中,任何可变数据应当只有一个相对应的唯一“数据源”
多个组件反映相同的变化数据时,共享状态提升到最近的共同父组件
state 只能由拥有它们的组件修改,bug 排查范围被大大缩减
state 和 props 都是原生的 JavaScript
对象
state 和 props 的变化都会触发生命周期、useEffect / useLayoutEffect、和渲染
state 和 props 相同,渲染结果相同
state 和 props 都可以在组件内部设置默认值
状态是一个 JavaScript
对象,状态名称即对象的属性名称
从 ECMAScript 2015 开始,对象初始化语法开始支持计算属性名。在 [] 放入表达式,计算结果当做属性名。例如
setState(updater, [callback])
setState(stateChange[, callback])
setState 的第二参数为可选回调函数
React
官方推荐使用 componentDidUpdate() 生命周期代替 setState 的回调函数setState 会合并当前状态与之前状态
replaceState 会丢弃之前状态,用新状态替代
replaceState 等同于先在 setState 中将状态设置为 false / null,再设置新状态
通过 setState(updater, [callback]) 的用法,第一参数使用带有形式参数的函数
通过 updater 函数 (state, props) => stateChange 的第一参数,接受原来的 state 状态值
对比新旧状态值
相同,返回 null,不渲染
不同,返回新状态值,触发异步合并渲染
示例代码
避免使用 Object 作为 State 值,使用 PureComponent 的浅比较的自动优化失效
必要使用 Object 作为 State 值
已经使用 Object 作为 State 值,并且嵌套层级过多
React
的属性?属性是组件的入参,用法同 HTML 自定义属性,可将任意类型数据从父组件传给子组件
属性的改变可以触发组件的生命周期流程和渲染
建议从组件自身的角度,不依赖于调用中间的上下文命名 Props
属性具有只读性,所有 React
组件必须像纯函数一样保护它们的 props 不被更改
请避免使用匿名函数作为属性值,避免引起重复渲染
具有 render prop 的组件接受一个返回 React
元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑
React
是“单向”数据流,数据通过 props 传递
从 state 派生数据或 UI 只能影响“低于”它们的组件,设计简单高效,便于调试
所有 React
组件必须像纯函数一样保护它们的 props 不被更改,保证组件没有副作用
props.children
render props
key 用来帮助 React
识别哪些元素改变
key 在数组列表及兄弟节点之间必须唯一
不建议使用索引作为 key 值,如果不显示指定 key 值,默认使用索引作为 key 值
key 只有放在就近的数组上下文中才有意义
key 不会传递给子组件,需要使用 key 属性的值,需使用其他属性显式传递
React
中进行静态类型检查?React
.PropTypes 或 prop-types 库
Flow
JavaScript
代码的静态类型检测器React
一起使用React
组件提供注解TypeScript
JavaScript
的类型超集,包含独立编译器React
的 JSX
使用.tsx作为扩展名新语法和工具链
React
中,可以在任何 PropTypes 属性后加上 isRequired,声明该属性必须
当必须属性没有被提供值时,控制台打印警告信息
表示该属性必须,而不限制属性类型,可以使用 PropTypes.any.isRequired
表示必须包含一个元素,可以使用 PropTypes.element.isRequired
React
中,通过特定 defaultProps 属性来定义 props 默认值
设置类组件或函数组件的静态属性 defaultProps
类组件,参考 proposal-class
-fields 提案,在 class
内声明静态属性 defaultProps
当传入属性值为 undefined 时,使用属性的默认值
React
是否支持 HTML 属性?React
16 中,任何标准和自定义的 DOM 属性都是完全支持
React
为 DOM 提供了一套以 JavaScript
为中心的 API
React
与 HTML 之间部分属性存在差异
class
Nmae
class
React
中,使用 Web
Components,使用 class
属性代替React
为浏览器 DOM 提供 innerHTML 的替换方案JavaScript
关键字React
元素使用 htmlFor 代替<input>
,<select>
和 <textarea>
元素的值并提交更改时 change 事件在这些元素上触发React
依靠该事件实时处理用户输入 <option>
标记为已选中状态,请在 select 的 value 中引用该选项的值JavaScript
对象,而不是 CSS 字符串JavaScript
属性一致,同时会更高效,且能预防跨站脚本(XSS)的安全漏洞React
自动添加 "px" 后缀到内联样式为数字的属性
React
发出警告React
服务端渲染与客户端渲染不同内容时发出警告<input>
,<select>
,<textarea>
组件支持 value 属性React
是否支持自定义属性?React
16 前会忽略未知的 DOM 属性。JSX
属性 React
无法识别,将被跳过
React
16 中任何未知的属性都会在 DOM 显示,适用于
Web
ComponentsReact
父子组件通信有哪些方法?props
refs
React
组件实例或 DOM 元素context
事件订阅发布
使用 Node.js 的 events
使用状态管理,举例说明特点
React
是单向数据流?React
组件不关心其他组件是否有状态和类型
React
中这种自上而下的数据传递被称为单向数据流
单向数据流单向绑定无关,对比双向绑定
React
开发者工具来检查问题组件的 propsContext 提供了一种无需为每层组件手动添加 props,能在组件树间进行数据传递的方法
Context 设计目的是共享或缓存组件树的全局数据,如用户认证,地区偏好,主题和语言
Context 应用场景是不同层级组件访问同样数据,副作用是降低组件的复用性
Context API
React
.createContext 创建 Context 对象,订阅该对象的组件从组件树中离自身最近的匹配的 Provider 中读取当前的 context 值React
节点
ContextType 用于订阅单一的 context
class
.contextType 为 React
.createContext() 创建的 context 对象class
-fields 提案在 React
组件中,使用 this.context 访问通过 contextType 指定的 Context
Context 的 value 更新,它内部所有消费组件都会重新渲染,并且不受制于 shouldComponentUpdate 函数
context 使用参考标识(reference identity) 来决定渲染时机,当 Provider 接收 value 为对象字面量时,每次 value 都会被赋值新对象,建议将 value 状态提升到父节点的 state 里
拆分 Context
将原来同一个 Context 频繁变化的值拆分出来,分别放入不同的 Context
记忆化
使用 React
.memo 或 useMemo 包裹组件,指定需要比较的值,只有值变化时重新渲染
使用 createContext 的第二参数,不稳定,不推荐
createContext 为函数,两个形参分别为更新前后值,接收 Number 类型作为返回值
在订阅 context 变化的组件上,使用 unstable_observedBits 设置重新渲染组件的条件
使用第三方库 use-context-selector 使用 createContext 创建支持 useContextSelector 的特殊 context 使用 useContextSelector 选择 context 某个值,当且仅当该值变化时,重新渲染
React
Tracke
Ref 转发可以将 ref 传递到子组件
React
.forwardRef 实现React
组件实例或 HTML DOM 元素Ref 转发适合应用场景
Ref 转发更改了组件默认的 ref 指向,对组件使用者不可见,不建议使用
由 React
.forwardRef 实现 Ref 转发,可以使用函数决定 ref 转发组件显示的内容
React
.forwardRef 函数名称,例如设置函数的 displayName 来包含被包裹组件的名称
React
返回空对象有哪些方法?React
.Fragment or <></>
优化状态 state
React
可能会把多个 setState 调用合并成一个调用优化属性 props
先比较再更新
React
.memo 比较 props 再渲染组件
减少嵌套
React
.Fragement或其缩写<>
惰性渲染
React
精确判断变化,复用不变元素优化 Context,减少 value 更新,内部所有消费组件的重新渲染
React
.memo 或 useMemo 包裹组件,指定需要比较的值,只有值变化时重新渲染React
TrackeReact
如何渲染 HTML ,有什么风险?React
为浏览器 DOM 提供 innerHTML 的替换方案React
设计此替换方案,通过名称警示开发者React
为什么要引入基于 Fiber 协调器的异步渲染?在经典渲染模式下,React
一旦开始渲染一次更新,不能中断包括创建新 DOM 节点和运行组件中代码在内的工作,这种渲染方法为“阻塞渲染”
渲染阻塞响应用户输入和动画,使用防抖牺牲一定的响应即时性,使用节流降低更新频率,都不能提供最佳用户体验。
React
提供 Concurrent 模式,将人机交互研究的结果整合到真实 UI 中
React
可以在旧屏幕桑多停留一段时间,跳过不够好的加载状态,直接展示新屏幕React
Fiber ?React
组件是数据的函数 v = f(d)
计算机通过调用栈跟踪程序执行
React
Fiber 是专门为 React
组件实现的堆栈重构
JavaScript
对象
class
或字符串class
的 render 方法返回值class
或函数组件返回数组,其中包含多个子节点的情况React
应用的叶子节点,浏览器环境是小写 HTML 标签React
Fiber 异步渲染分为哪几个阶段,对应生命周期是什么?不同于 Stack renconciler,Fiber reconciler 过程分为两个阶段:
对应生命周期
React
组件有哪些生命周期方法?React
每个组件都包含“生命周期”方法
开发者可以重写这些方法,便于在运行过程中特定阶段执行这些方法
React
组件挂载之前调用React
组件调用 render 方法前,初始挂载及后续更新都会被调用React
参考返回 true / false,决定是否重新渲染class
组件中唯一必须实现的方法,纯函数,每次调用应返回相同结果React
元素:通过 JSX
创建React
组件的生命周期可分为哪些阶段?React
组件的生命周期可以分为三个阶段
"Render"阶段:纯净且不包含副作用,可能会被 React
暂停、中止或重新启动
"Pre-commit"阶段:可以读取DOM
"Commit"阶段:可以使用 DOM,运行副作用,安排更新
通过网络请求获取数据或订阅数据更新
有条件地通过网络请求数据
取消网络请求或者清除在 componentDidMount() 中创建的订阅
useLayoutEffect
useEffect
执行时机是组件挂载或更新之后,浏览器完成布局和绘制之后,在一个延迟事件中被调用
支持返回清除函数,函数执行时机是组件卸载之前
两者都适用于在函数组件主体内,即 React
渲染阶段改变 DOM,添加订阅,设置定时器,记录日志以及执行其他包含副作用的操作
优先使用 useEffect 避免阻塞视觉更新,只在需要读取 DOM 布局,在浏览器绘制前,同步触发重渲染的场景使用 useLayoutEffect
JavaScript
中,super 指向父类构造函数,React
类组件可以是 React
.Component 等
JavaScript
中,调用 super 前不能用 this。React
中不能声明 this.state 或读取 this.props
React
中,调用 super(props) 后,可以在 constructor 中访问 this.props
React
中,调用 super() 不传参,不能在 constructor 中访问 this.props
React
在调用构造函数后,将 props 赋值到实例不调用 super,参考 proposal-class
-fields 提案,可以将 state 等实例属性写在 class
内
React
Hook 与生命周期什么是 React
Hook
React
Hook 是 React
16.8 的新增特性React
特性为什么要用 React
Hook 代替拥有生命周期的类组件
React
没有提供将可复用性行为“附加”到组件的途径React
Hook 解决的问题
React
特性React
.memo 比较 props 再渲染组件
React
和 DOM 事件处理的区别是?React
元素的事件处理和 DOM 元素的不同点
事件命名
React
:小驼峰式事件处理函数传参
React
:接受函数阻止默认行为
React
:显式的使用 preventDefault事件处理函数,第一参数 e
React
:e 是一个合成事件。React
根据 W3C 规范来定义合成事件添加事件监听
React
:元素初始渲染的时候添加监听器this 指向
React
:在 class
组件中,手动 bind this,使用匿名箭头函数或者 class
fields 语法来让 this 指向组件本身向事件处理程序传递参数
React
:通过箭头函数和 Function.prototype.bind 来实现事件委托
React
:16 及更早版本,对大多数事件执行 document.addEventListener() 17 后调用 rootNode.addEventListener()React
合成事件?React
合成事件是浏览器的原生事件的跨浏览器包装器
事件池
React
16 及更早版本,合成事件是合并而来,放入事件池统一管理
React
17 合成事件不再放入事件池
事件执行顺序
合成事件会冒泡
React
16 及更早版本,冒泡绑定到 document 上React
17,冒泡到 rootNode 上,onScroll 事件不再冒泡React
事件处理阻止默认行为?调用e.preventDefault
阻止默认行为
使用 Function.pototype.bind,添加事件处理函数时,给函数绑定 this
使用 public class
fields 语法,可以使用 class
fields 正确的绑定回调函数
使用 匿名回调函数 添加事件,不推荐,原因是
两种方式
箭头函数
Function.pototype.bind
原生 +
React
Hook
原生 +
React
Hook
React
17 对事件处理做了哪些改进?更改事件委托
React
树嵌套,多版本都必须为 17 或更高版本
React
16 及之前版本
React
17
React
树的根 DOM 容器附加事件处理器React
嵌入使用其他技术构建的应用程序变得更加容易
React
16 及之前版本
React
版本破坏,嵌套树结构中阻止了事件冒泡,但外部树依然能接收到它React
17 的事件冒泡更接近常规 DOM,更符合预期
React
版本的事件冒泡,包括前套树和外部树对标浏览器
React
16 及之前版本
React
实现React
实现React
17 与浏览器行为更接近,并提高了互操作性
去除事件池
React
16 及以前版本
React
17 去除事件池,事件对象行为符合预期
React
中使用样式?Style
React
自动添加 px 后缀到内联样式为数字的属性之后class
Name 属性来引用外部 CSS 样式表中定义的 class
Class
class
Name 支持字符串class
Name 支持大括号包裹的 JS 表达式CSS-in-JS
JavaScript
生成而不是在外部文件中定义React
对样式如何定义没有明确态度
class
Name 指定它们动画
React
Transition GroupReact
Motion 和 React
Springstyle 采用小驼峰命名属性的 JavaScript
对象,可以按照 JavaScript
对象的方式,有条件地改变属性名和属性值
class
Name 属性,可以传入 JavaScript
表达式,有条件改变的类名
以styled-components 为例
模板字符串
JavaScript
对象
style采用小驼峰命名属性的 JavaScript
对象
任何合并 JavaScript
对象的方法都可以用于合并内联样式
(1)什么是 CSS 模块化?
CSS 模块化是将 CSS 规则 拆分成相对独立的模块,便于开发者在项目中更有效率地组织 CSS
CSS 模块化的方式
基于文件拆分
不拆分但设置作用域
CSS in JS
内联样式、Shadow DOM 等
无论哪种方式,核心都是通过 保证CSS类命名唯一,或者 避免命名使用内联样式,来模拟出CSS模块作用域的效果
(2)基于文件的 CSS 模块的加载
<link/>
将不同模块的 CSS 分文件存放,通过 标签按需引入import
在 Web
pack 中,将 CSS 作为资源引入,通过 CSS Modules 生成独一无二的类名(3)CSS 模块化的实现方式
分层拆分
将 CSS规则 分层存放,并约束不同层次间的命名规则
分块拆分
将页面中视觉可复用的块独立出来,仅使用类选择器,并确保类名唯一
原子化拆分
每个选择器只包含 1 个或少量属性,通过组合选择器定义复杂样式
CSS in JS
Shadow DOM
通过attachShadow给元素的影子DOM,附加<style>
标签,其中规则不会影响外部元素。代表的框架有 Ionic 等
(4)React
中的样式模块化
React
对样式如何定义,没有明确的态度,如果存在疑虑,比较好的方式是和平常一样,在一个单独的 *.css 定义你的样式,并通过 class
Name 指定它们。此时,你可以参考 CSS 模块化的方案,通过文件或样式命名规则,来实现样式模块化
行内样式 style 是避免样式命名冲突的经典策略,但由于性能比 class
Name 低,React
通常不推荐将 style 属性作为设置元素样式的主要方式。在多数情况下,应使用 class
Name 属性来引用外部 CSS 样式表重定义的 class
。style 在 React
应用中多用于在渲染过程中添加动态计算的样式
同样地,在 CSS-in-JS 中,早期有基于 style 实现的 Radium 库,现在比较流行的是基于随机 class
Name 的 style-components 库,与之类似的有 Emotion 和 Glamor,还有面向愿意将 CSS 和 JavaScript
分开存放开发者的 JSS 等
React
错误边界?(1)为什么要有错误边界?
部分 UI 的 JavaScript
错误不应该导致整个应用崩溃,为了解决整个问题,React
16 引入了错误边界的概念
(2)什么是错误边界?
错误边界是一种 React
组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript
错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。
错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。
(3)如何构建一个错误边界组件?
如果一个 class
组件中定义了 static getDerivedStateFromError() 或 componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界,其中
使用只需用错误边界组件包裹 React
组件
错误边界可以捕获子组件的错误,包括子组件
以上其中的错误
错误边界不能捕获以下场景产生的错误:
事件处理
异步代码
服务端渲染
自身抛出来的错误
错误边界的位置通常由开发者决定,可以用错误边界组件包裹
组件有两个声明周期方法可以用于错误捕获
componentDidCatch(error, info)
此生命周期在后代组件抛出错误后被调用
它将抛出的错误作为第一参数
error
它将带有
componentStack
key 对象,即包含有关组件引发错误的栈信息作为第二参数info
此生命周期会在提交阶段调用,允许执行副作用,如打印、记录或上报错误日志等
React
如何处理未捕获错误,为什么这样处理?(1)React
如何处理未捕获错误
自 React
16 起,任何未被错误边界捕获的错误都将会导致整个 React
组件树被卸载。在浏览器环境使用 React
Dom 渲染的页面将展示为白屏,并在控制台输出报错信息
(2)为什么这样处理?
React
认为把一个错误的 UI 留在那比完全移除它更糟糕。在即时通信应用中展示错误的消息,在支付类应用中展示错误的金额,都比不呈现任何内容更糟糕
白屏有助于开发者发现已存在但未曾注意到的崩溃,手动增加错误边界,让开发者应用发生异常时提供更好的用户体验
React
推荐使用 JS 错误报告服务,了解在生产环境中出现的未捕获异常,并将其修复
错误边界无法捕获事件处理函数内部的错误,因为事件处理不会在渲染期间触发,即使异常,React
仍然能够知道需要在屏幕上显示什么
捕获事件处理函数内部的错误,使用 JavaScript
的 try / catch 语句即可
事实上,我们可以用 window.addEventListener('error', () => {})
捕获大部分同步、定时器、Generator 异步错误,用 window.addEventListener('unhandledrejection')
捕获 Promise 及其语法糖 async / await 错误
React
Hook?Hook 是 React
16.8 的新增特性
允许开发者在函数组件里“钩入”React
state 及生命周期等特性的函数
React
内置如 useState、useEffect 等 HookHook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
开发者可以在不编写 class
的情况下使用 state 以及其他的 React
特性
class
组件的区别和应用场景Hook 和现有代码可以同时工作,渐进式地使用
State Hook 允许开发者在 React
函数组件中添加 state 的 Hook
这种 Hook 在 React
的原生实现是 useState,它是一种函数调用时保存变量的方式,它与 class
里面的 this.state 提供的功能完全相同
useState 的唯一参数是初始 state,支持数字、字符串、对象等类型
useState 方法的返回值分别是当前 state 以及更新 state 的函数,使用数组解构获取赋值
state 只在组件首次渲染时创建,下次重新渲染时,返回当前的 state
Effect Hook 允许开发者在函数组件中执行副作用操作,包括数据获取、设置订阅以及手动更改 React
组件中的 DOM。分为需要清除和不需要清除的
这种 Hook 在 React
的原生实现是 useEffect
useEfect 支持两个参数
class
组件的 componentDidMount、componentDidUpdate 生命周期 + 浏览器完成画面渲染时useEffect 可以返回一个函数,用于移除订阅等副作用,区别 class
组件的 componetWillUnMount,React
会在执行当前 effect 之前对上一个 effect 进行清除
useEffect 放在最贱内部可以直接访问 state 和 props
在 useEffect 的 effect 函数中,返回一个清除函数
effect 在每次渲染的时候会执行,
React
会在执行当前 effect 之前对上一个 effect 进行清除
Hook 本质是 JavaScript
函数,使用 Hook 时需要遵循两条规则
只在最顶层使用 Hook
React
函数的最顶层调用 Hook只在 React
函数中调用 Hook
React
的函数组件,而不要在普通的 JavaScript
函数中调用 HookReact
提供了一个名为 eslint-plugin-react-hooks 的 ESLint 插件来校验 Hook 是否遵循规则
useMemo
返回一个memoized值,把"创建"函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销计算。 useCallback
返回一个memoized回调函数,把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。 useCallback 可看作函数组件的shouldCompoentUpdate,使用引用相等性避免非必要渲染
useCallback 返回的是函数,useMemo 返回的是值,也可以是函数
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
useReducer
useReducer 是 useState 的替代方案,它接收一个形如(state, action) => newState的 reducer,返回当前的 state 及其配套的dispatch方法 useReducer 比 useState 更适用于某些场景
useReducer 第二个返回值是 dispatch 而不是回调函数
useLayoutEffect
相同
useLayoutEffect 的签名与 useEffect 相同
服务端渲染时,useLayoutEffect 和 useEffect 都无法在 Javascript 代码加载完成前执行
React
告警,解决
区别
useRef
useRef 返回一个可变的 ref 对象,其.current属性被初始化传入参数initialValue 返回的 ref 对象在组件的整个生命周期保持不变
Refs
Refs 使用 React
.createRef() 创建的,并通过 ref 属性附加到 React
元素
当 ref 被传递给 render 中的元素时,对节点的引用可以在 ref 的 current 属性中被访问
区别
useRef
函数组件可用
用途多样,useRef() 创建一个普通 Javascript 对象,每次渲染时返回同一个 ref 对象
Refs
class
组件或 HTML 元素,函数组件无实例,不可用
用途单一,.current 属性为实例的引用,根据节点的类型不同
React
.createRef() 创建的ref接收底层 DOM 元素作为 current 属性class
组件时,ref 对象接收组件的挂载实例作为其 current 属性(1)自定义 Hook 定义
自定义 Hook,是将组件逻辑提取到可重用的函数,它可以像 render props 和高阶组件来共享组件之间的状态逻辑,而不增加组件
(2)如何自定义 Hook
自定义 Hook 是一个函数,可以调用其他的 Hook
名称以 "use" 开头,表示这是一个 Hook
React
判断函数函数内部是否包含对其内部 Hook 的调用参数和返回可以自定义,可以像使用函数一样在不同 Hook 间传递信息
其中所有 state 和副作用完全隔离,每次调用 Hook,都会获取独立的 state
将两个函数组件的共同的状态逻辑提取到自定义 Hook 中,自定义 Hook 是一种自然遵循 Hook 设计的约定,而不是 React
的特性
(3)自定义 Hook 作用
自定义 Hook 解决了以前在 React
组件中无法灵活共享逻辑的问题
自定义 Hook 可以用于表单处理、动画、订阅声明、计时器等场景
尽量避免过早地增加抽象逻辑,当函数组件代码行数增多时,可以通过自定义 Hook 简化代码逻辑,解决组件杂乱无章
不,React
推荐称为编写 React
组件的主要方式,并且提供了自定义 Hook 等 class
组件无法实现的功能,更利于自动代码优化。但是
Hook 暂时不能覆盖 class
组件的所有场景,比如生命周期getSnapshotBeforeUpdate,getDerivedStateFromError 和 componentDidCatch 暂无 Hook 等价写法
没有计划从 React
中移除 class
所以,React
鼓励写新组件的时候开始尝试 Hook,不推荐用 Hook 重写已有的 class
,除非开发者本来就打算重写它们(例如:为了修复 bug)
useState 返回更新 state 函数式异步的,接收参数可以是新状态的值,也可以是回调函数
可以通过两种方式,获取上一个 state 的状态值
回调函数的第一个参数是上一个状态的值,可以像使用 useReducer 的 reducer 函数一样,基于上一个 state 来生成新的 state
或者可以在 useEffect 或 useLayoutEffect 中获取更新后的状态,将 useEffect 或 useLayoutEffect 封装到自定义 Hook 同样有效
Jest 是一款 JavaScript
测试运行器,零配置,支持快照,并行,迭代快,生态丰富
Jest 支持模拟或者真实的 web 标准环境
JavaScript
实现,用于 Node.js 提供仿真 DOM 环境React
组件Jest 拥有丰富的 Mock Functions API,可以模拟 modules 和 timers 精细控制代码执行
Jest 提供足够的 Exceptions 方法,测试失败时,并提供丰富的上下文信息
Jest 只需添加 --coverage 参数,即可生成完整的测试覆盖率报告
可以防止由于后端不可用导致的测试不稳定
加快测试运行速度
模拟数据获取
可以用 jest.mock 来模拟组件
使用 jest.useFakeTimers()开启全局模拟计时器
使用 jest.runAllTimers() 快进,直到所有计时器都被执行
使用 jest.runOnlyPendingTimers() 快进,直到当前被挂起的计时器都被执行,不包含在此过程中创建的任何新计时器,避免递归计时器死循环
使用 jest.advanceTimersByTime(milliseconds) 快进指定毫秒数,直到毫秒数之前的计数器都被执行
浅层渲染即只渲染父组件而不渲染所有子组件,可以只渲染组件的“第一层”
浅层渲染的实现
组件:MyCompoent.js
react-test-renderer 与 jest 的快照功能一起用于简化 React
组件测试
测试通常在无法访问真实渲染表面(如浏览器)的环境中运行
建议使用 jsdom 来模拟浏览器,这是一个在 Node.js 内运行的轻量级浏览器实现
Web
的组件测试仍然有用,因为它的运行比每个测试启动浏览器的方式效率更高综上,可以使用 Jest 作为测试运行器,渲染到 jsdom,使用 act() 辅助函数提供的能力通过一系列的浏览器事件来模拟用户交互行为,此外
前端 javascript 测试代码覆盖率的常用工具为 istanbul
使用 jest 框架,可以传入参数 --coverage 获得测试代码覆盖率
React
Router?测试 React
Router,需要先使用 createMemoryHistory() 来创建导航历史,模拟点击导航按钮,等待页面 DOM 加载,判断应加载页面的独有元素是否被加载
以 @tesing-library/react 为例
App
.jsx
App
.test.js
Virtual DOM 是一编程概念
UI 以一种理想化的,或者说“虚拟的”表现形式被保存在内存中
通过 React
DOM 等类库使之与“真实的” DOM 同步,这一过程叫做协调
React
Canvas 和 React
Native 等其他渲染方式,甚至非浏览器环境Virtual DOM 赋予 React
声明式的 API
告诉 React
希望让 UI 是什么状态,React
就确保 DOM 匹配该状态
开发者不必关心属性操作、事件处理和手动 DOM 更新这些构建应用程序必要的操作
Virtual DOM 是一种模式,在 React
中
Virtual DOM 通常与 React
元素关联,代表用户界面的对象
fibers 内部对象来存放组件树的附加信息
React
Fiber 是协调引擎,主要目的是使 Virtual DOM 可以增量式渲染
React
Diff,对比 Vue Diff ?虚拟 DOM 的 Diff 算法
将新旧虚拟 DOM 看作两棵节点树,节点个数为 n
综上,Diff 虚拟 DOM 的复杂度是 O(n³)
React
基于以下两个假设的基础之上提出 O(n) 的启发式算法
React
Diffing 算法
React
会拆卸原有的树并且建立起新的树React
会保留节点React
继续对子节点进行递归React
将更新该组件实例的 props 以及保证与最新的元素保持一致React
会同时遍历两个子元素的列表,递归 DOM 节点的子元素
React
元素,保留 DOM 节点,仅对比及更新改变的属性Vue 2.x 优化 Diff 算法
基本优化与 React
相同
pathNode
updateChildren
Vue 3.x 优化 Diff 算法
创建 VNode 确定类型,内容不会变化的 DOM 添加静态标记
在 mount / patch 中用位运算判断 VNode 类型
React
Concurrent 模式?Concurrent 模式是一组 React
的新功能,可帮助应用保持响应,并根据用户的设备性能和网速进行适当的调整
Concurrent 模式的特点包括
可中断渲染
有意的加载顺序
React
首先在内存中准备新屏幕React
继续显示完全互动,带有内联加载指示器的旧屏幕React
可以带我们跳转到新屏幕并发
React
可以同时更新多个状态
React
甚至可以在全部数据到达之前就在内存中开始渲染Concurrent 模式的任务是帮助将人机交互研究的结果整合到真实的 UI 中
Concurrent 模式的开启
React
实验版本开启 Concurrent 模式
React
的 legacy 模式
React
DOM.createBlockingRoot(rootNode).render(<
App />
)React
默认开发模式
React
DOM.createRoot(rootNode).render(<
App />
)Concurrent 模式常用 API
Suspense
SuspenseList
useTransition
useDeferredValue
Suspense 让组件“等待”某个异步操作,直到该异步操作结束即可渲染
Suspense 不是一个数据请求的库,而是一个机制
React
通信说明某个组件正在读取的数据当前仍不可用React
可以继续等待数据的返回并更新UISuspense不是什么
Suspense可做什么
React
紧密整合Suspense 可以不必等到数据全部返回才开始渲染
React
会跳过“挂起”组件,继续渲染组件树中的其他组件React
将尝试重新渲染,并且每次都可能渲染出更加完整的组件树React
如何定义任务的优先级?任务优先级是产生更新对象之后,React
去执行一个更新任务,这个任务的优先级
任务优先级被用来区分多个更新任务的紧急程度,对比前后两次更新的任务优先级
后者 > 前者,React
会取消前者的任务调度
后者=前者,React
会将同等优先级的更新收敛到一次任务中
后者 < 前者,React
会在前者更新完成后,再对后者发起任务调度
简而言之,任务优先级存在的意义
React
定义的任务优先级分为三类
同步优先级:React
的 legacy 同步渲染模式产生的更新任务的优先级
同步批量优先级:React
的 blocking模式产生的更新任务的优先级
Concurrent 模式优先级:对于用户输入、悬停、点击、页面转换等在内部使用不同的“优先级”,大致对应于人类感知研究中的交互类别
LanePriority 越大,优先级越高
Redux 是 JavaScript
应用的可预测状态容器
可预测
集中
可调试
灵活
Flux
Flux 是用于构建用户界面的应用程序架构,通过单向数据流补充 React
可组合的视图组件
Flux 更像模式而非框架,没有任何硬依赖
Flux 架构的应用包含 4 部分
Action
通过 Action creators 创建
每个 Action 拥有 type 或类似属性
传递给 Dispatcher
Dispatcher
Store
接受 Action 更新数据后,触发 change 事件,通知 View
可以由多个 Store
View 视图组件,即 Controller-View
Redux
Redux 是 JavaScript
应用的可预测状态容器
Redux 对不同框架都有完整实现,Facebook 官方推荐使用代替 Flux
Redux 架构与 Flux 基本一致,但做了简化
State 只读,更改 State 的方式是返回新的对象,即引入 Reducer 纯函数
Action 与 Dispatcher ,只需返回包含 type 和 payload 属性的对象
Store
唯一
createStore 基于 Reducer 纯函数创建
store.dispatch() 调用 Action
View
通过 store.getState() 获取最新状态
通过store.subscribe() 订阅状态更新
综上,Redux 与 Flux 都基于单向数据流,架构相似,但 Redux 默认应用只有唯一 Store,精简掉 Dispatcher,引入 Reducer 纯函数,通过返回新对象,而不是更改对象,更新状态。
对比 Flux 的官方实现,Redux 的 API 更简洁,并且提供了如 combineReducers等工具函数及 React
-Toolkit 工具集,以及对状态的撤销、重做和持久化等更复杂的功能。提供如 React
-Redux 等简化 Redux 与其他第三方库的连接。
Facebook 官方推荐在生产环境中使用代替 Flux。
Redux 设计和使用遵循三个基本原则:
单一数据源,Store 唯一
状态是只读的
Reducer 是纯函数,用来归并状态 State
React
Context 和 Redux 的区别是?React
Context
React
Context API 是为了解决跨组件层级传递 props 的效率问题
试验性的 Context API存在问题
ContextAPI 正式在 React
16.3引入,使用方法
使用
<Provider value={``v``a``lu``e}>
组件去声明想要传递的数据源消费数据
高阶组件写法
<Consumer>{``v``aule=> ... }</Consumer>
Hook写法
const value = useContext(MyContext)
Redux
JavaScript
应用的可预测状态容器创建store 对象
使用 state 和 dispatch
区别
目的
React
Context 解决跨组件层级传递 props 的效率问题JavaScript
应用的可预测状态容器,拥有完整的状态管理功能更新机制
React
Context:Context 的 value 更新,它内部所有消费组件都会重新渲染,并且不受制于 shouldComponentUpdate 函数,需要手动优化
调试
React
Context支持 React
DevTools 调试中间件
React
Context不支持中间件React
访问 ReduxStore 的方法有哪些?React
组件访问 ReduxStore引入sotre 通过 getState获取状态
异步请求数据等异步操作通常要发出三种 Action
操作发起时 Action,以 start 为例
操作成功时 Action,以 success 为例
操作失败时 Action,以 failure 为例
发送多 Action 方法
main.js
并非所有应用程序都需要 Redux,是否引入 Redux 由以下决定
Redux 可以共享和管理状态
通过可预测的行为来帮助回答:状态何时、何处、为什么及如何改变
增加概念、代码和限制,增加学习成本和项目复杂度
平衡利弊,在以下情况引入 Redux 最有用
小型项目
大型项目 提取公共组件,进一步拆分 actions 为同步 actions.js 和异步 sagas.js
React
的工程项目脚手架及最佳实践样板React
、Relay和GraphQL构建Web
应用程序的前端模板每个页面或容器组件 index.jsx 注入 modules/reducer 到 store
redux-thunk 允许 dispatch一个函数,而不是返回 action 的动作创建器
thunk 可用于延迟动作的调度,或仅在满足特定条件时才调度
内部函数接收 dispatch 和 getState 方法作为参数
源码如下
Next.js 是一款用于生产环境的 React
框架
Next.js 提供生产环境所需的所有功能和最佳开发体验
静态及服务器端融合渲染
支持 TypeScript
智能化打包
路由预取
Next.js 的特点
默认情况下,Next.js 预渲染每个 页面,即为每个页面生成 HTML 文件,无需客户端 JavaScript
渲染。从而带来更好的性能和 SEO 效果
每个生成的 HTML 页面都与该页面所需的最少 JavaScript
代码关联。当浏览器加载一个页面时,其 JavaScript
代码将运行并使页面完全具有交互性
Next.js 预渲染有两种形式,主要是页面生成时机不同:
静态生成(Static Generation)
服务器端渲染(Server-side Rendering)
Next.js 的路由基于文件系统,每个 pages 目录下的组件都是一条路由
首页路由
嵌套路由
动态路由
API 路由
浅路由
Next.js 的路由面向生产环境设计,简单直观,开箱即用,前后端统一管理,利于维护
getInitialProps
getStaticProps
getStaticPaths
getServerSideProps
上传 .next/static 文件夹到 BOS 或服务器,绑定 CDN 域名,可以是主域名是子域名
打开 next.config.js 添加 assetPrefix 配置,注意区分生产环境
特性
提炼自企业级中后台产品的交互语言和视觉风格
开箱即用的高质量 React
组件
使用 TypeScript 开发,提供完整的类型定义文件
全链路开发和设计工具体系
数十个国际化语言支持
React
组件 ConfigProvider 用于全局配置国际化文案深入每个细节的主题定制能力
兼容环境
现代浏览器和 IEII
支持服务器渲染
支持 Electron
可选 Ant Design Mobile 支持移动端
React
/ React
Native社区支持
Ant Desgin of Angular
/ Ant Design of Vue
完整文档 / 大厂背书 / 众多用户,容易找到解决方案
为 Table 设置 scoll={{ y: 任意数值 }} 开启固定表头
AntD 使用两个的 div 分别嵌套 table 实现固定表头效果
通过 react-window 虚拟滚动方案引入 VariableSizeGrid
构建 renderVirtualList 函数组件,接收 rawData, { scrollbarSize, ref, onScroll } 作为参数
引入antd 的 Table 组件,设置 components 属性为 {{ body: renderVirtualList }}