概述
Hello, 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个层级
- 不要过度思考
- 没有官方推荐的组织方式,常见组织方式包括
按功能和路由组织:相同功能或路由的 CSS、JS 和 测试文件放入同一目录
按文件类型组织:组件、页面、API、状态管理、静态文件分类存放
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
全新的 SSR 架构内置支持 React.lazy() 和 React.Suspense
- startTransition
-
React 17 及以前版本
-
所有更新都被紧急渲染
-
使用 setTimeout 和 防抖等方式,避免频繁更新
-
React 18
-
所有渲染分为紧急和非紧急
-
非紧急渲染使用 startTransition 包裹
-
非紧急渲染的延迟时间由设备决定
-
非紧急渲染可中断,不会影响响应用户输入、动画等紧急渲染
什么是 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 逻辑内在耦合
UI 中绑定处理事件
状态更新时通知 UI
UI 中展示数据
- React 将标记和逻辑共同存放在组件,实现关注点分离
- React 不强制要求使用 JSX,但 JSX 与 UI 一起有视觉辅助作用
- React 通过 JSX 可以显示更多有用错误和警告消息
为什么 JSX 可以有效降低 XSS 风险?
- React DOM 在渲染所有输入内容前,默认会将它们转义成字符串,有效降低 XSS 风险
- 可以通过dangerouslySetInnerHTML = {{ __html: HTML }}来显示转义前的内容
如何在 JSX 中条件渲染?
- if/else语句
render() {
if (condition) return <div />
else return null
}
- 三元运算符
render() {
return condition ? <div /> : null
}
- 逻辑运算符
render() {
return condition && <div />
}
- 条件渲染组件
render () {
const Condition = props => {
const { If, children } = props
return If && children
}
return <Condition If={true}><div /></Condition>
}
如何在 JSX 中循环控制?
- map 将数组每一项转成 UI,flatMap filter 等能返回数组的循环方法也被支持
render() {
const { data } = props
return data.map(item => <b id={item.id}>{item.text}</b>)
}
- 使用 javaScript 的循环(for / while / do while),将结果存储到变量,将变量代入 JSX
render() {
const { data } = props
const datas = Array(data.length)
for (let i = 0; i < data.length; i++) {
const item = data[i]
datas[i] = <b id={item.id}>{item.text}</b>
}
return datas
}
- 使用 lodash 等第三方库或自定义可以返回数组的方法,以 lodash 为例
render() {
const { data } = props
return _.times(data.length, i => <b id={data[i].id}>{data[i].text}</b>)
}
为什么 JSX 中 class 变成了 className ?
- JSX 语法上更接近 JavaScript 而不是 HTML
HTML 属性值通常为字符串
HTML DOM 对象属性值可以是任意数据类型
- JSX 的 className 更接近 HTML DOM 对象的属性,并且支持属性拓展运算符
- JSX 通过匹配闭合标签提升可读性,而不是代替 HTML
JSX 与 HTML 需要转换,直接使用 class 也无法避免其它转换工作
什么是 React 组件?
- React 组件允许用户将 UI 拆分成独立可复用的代码片段,并对每个片段进行独立构思
- React 组件从概念上类似于 JavaScript 函数
- React 组件接受任意的入参 Props,返回用于描述页面展示内容的 React 元素
React 组件分成哪几类?
- 按定义分类
类组件,使用 ES6 的 class 定义,维护 state,有生命周期
函数组件,使用普通函数定义,可以通过 hooks 维护状态和副作用
- 按状态分
有状态组件,组件返回结果,受时间、空间或上下文影响
无状态组件,通常是纯展示 UI 组件,容易复用
- 按定位分
展示型组件,接收 props,负责 UI 展示
容器组件,管理 states,负责数据获取和组件间通信,多用于状态提升
按 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