1.JSX是什么,它和JS有什么区别?
JSX 是 JavaScript 语法的扩展,它允许编写类似于 HTML 的代码。它可以编译为常规的 JavaScript 函数调用,从而为创建组件标记提供了一种更好的方法。
- jsx是React.createElement(component, props, ...children) 函数的语法糖
- 底层是使用babel-plugin-transform-react-jsx插件 将jsx的语法转化为js对象,判断是否是jsx对象或是否是一个组件,转化为对应的js对象(虚拟dom)
2.简述React的生命周期
简短版本
React生命周期方法是在组件不同阶段执行的特定方法。以下是一些常用的React生命周期方法:
- getDefaultProps:获取实例的默认属性
- getInitialState:获取每个实例的初始化状态
- componentWillMount:组件即将被挂载、渲染到页面上
- render:组件在这里生成虚拟的Dom节点
- componentDidMount:组件真正在被装载之后 运行中的状态
- componentWillReceiveProps:组件将要接受到属性的时候调用
- shouldComponentUpdate:组件接受到新属性或者状态时(可以返回false,接受属性不更新,阻止render调用,后面的函数不会被继续执行了)
- componentWillUpdate:组件即将更新,不能修改属性和状态
- render:组件重新描绘
- componentDidUpdate:组件已经更新
- componentWillUnmount:组件即将销毁
详细版本
React 的生命周期主要分为三个阶段:MOUNTING、RECEIVE_PROPS、UNMOUNTING
-
组件挂载时(组件状态的初始化,读取初始 state 和 props 以及两个生命周期方法,只会在初始化时运行一次)
- componentWillMount 会在 render 之前调用(在此调用 setState,是不会触发 re-render 的,而是会进行 state 的合并。因此此时的 this.state 不是最新的,在 render 中才可以获取更新后的 this.state。)
- componentDidMount 会在 render 之后调用
-
组件更新时(组件的更新过程是指父组件向下传递 props 或者组件自身执行 setState 方法时发生的一系列更新的动作)
-
组件自身的 state 更新,依次执行
- shouldComponentUpdate(会接收需要更新的 props 和 state,让开发者增加必要的判断条件,在其需要的时候更新,不需要的时候不更新。如果返回的是 false,那么组件就不再向下执行生命周期方法。)
- componentWillUpdate
- render(能获取到最新的 this.state)
- componentDidUpdate(能获取到最新的 this.state)
-
父组件更新 props 而更新
- componentWillReceiveProps(在此调用 setState,是不会触发 re-render 的,而是会进行 state 的合并。因此此时的 this.state 不是最新的,在 render 中才可以获取更新后的 this.state。
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
-
-
组件卸载时
- componentWillMount(我们常常会在组件的卸载过程中执行一些清理方法,比如事件回收、清空定时器)
新版的生命周期函数增加了 getDerivedStateFromProps,这个生命周期其实就是将传入的 props 映射到 state 中。在 React 16.4 之后,这个函数每次会在 re-render 之前调用,
getDerivedStateFromProps的作用是
- 无条件的根据 prop 来更新内部 state,也就是只要有传入 prop 值, 就更新 state
- 只有 prop 值和 state 值不同时才更新 state 值。
但是盲目使用这个生命周期会有一些问题
- 直接复制 props 到 state 上
- 如果 props 和 state 不一致就更新 state
3.React事件机制和原生DOM事件流有什么区别
react中的事件是绑定到document上面的,而原生的事件是绑定到dom上面的,因此相对绑定的地方来说,dom上的事件要优先于document上的事件执行
4.Redux工作原理
Redux 是 React 的第三方状态管理库,创建于上下文API存在之前。它基于一个称为存储的状态容器的概念,组件可以从该容器中作为 props 接收数据。更新存储区的唯一方法是向存储区发送一个操作,该操作被传递到一个reducer中。reducer接收操作和当前状态,并返回一个新状态,触发订阅的组件重新渲染。
5.SetState是同步还是异步的,setState做了什么
首先,同步和异步主要取决于它被调用的环境
这里的同步还是异步,指的调用setState方法后,是否能立刻拿到更新后的值
- 如果 setState 在 React 能够控制的范围被调用,它就是异步的。比如合成事件处理函数、生命周期函数 在合成事件和钩子函数中,多次调用setState 修改同一个值,只会取最后一次的执行,前面的会被覆盖
- 如果 setState 在原生 JavaScript 控制的范围被调用,它就是同步的。比如原生事件处理函数、setTimeout、promise的回调函数等。在原生事件和异步中,可以多次调用setState 修改同一个值,每次修改都会生效
6.react中的合成事件和原生事件
react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件,像在jsx中常见的onClick、onChange这些都是合成事件
原生事件
是指非react合成事件,原生自带的事件监听addEventListener,或者也可以用原生js、jq直接绑定事件的形式都属于原生事件
7.什么是fiber,fiber解决了什么问题
解决react旧版本,更新页面时会出现丢帧卡顿的问题
React旧版本问题
当我们调用setState更新页面的时候,React会遍历应用的所有节点,计算出差异,然后再更新 UI 整个过程是一气呵成,不能被打断的。如果页面元素很多,整个过程执行的时间可能超过 50 毫秒,就容易出现掉帧的现象
新版本解决方案
React Fiber
是把一个大任务拆分为了很多个小块任务,一个小块任务的执行必须是一次完成的,不能出现暂停,但是一个小块任务执行完后可以移交控制权给浏览器去响应用户操作
核心是通过 requestIdleCallback ,会在利用浏览器空闲时间会找出所有需要变更的节点
- 阶段一,生成 Fiber 树,得出需要更新的节点信息,这一步是一个渐进的过程,可以被打断
- 阶段二,将需要更新的节点一次性批量更新,这个过程不能被打断
8.react中使用了Fiber,为什么vue没有用Fiber?
原因是二者的更新机制不一样
- Vue 是基于 template 和 watcher 的组件级更新,把每个更新任务分割得足够小,不需要使用到 Fiber 架构,将任务进行更细粒度的拆分
- React 是不管在哪里调用 setState,都是从根节点开始更新的,更新任务还是很大,需要使用到 Fiber 将大任务分割为多个小任务,可以中断和恢复,不阻塞主进程执行高优先级的任务,如果不用Fiber,会出现老版本卡顿的问题
9.react中props和state有什么区别
props 是传递给组件的(类似于函数的形参),而 state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)
props 是不可修改的,所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。 由于 props 是传入的,并且它们不能更改,因此我们可以将任何仅使用 props 的 React 组件视为 pureComponent,也就是说,在相同的输入下,它将始终呈现相同的输出。 state 是在组件中创建的,一般在 constructor中初始化 state state 是多变的、可以修改,每次setState都异步更新的。
10.为什么虚拟 dom 会提高性能?
虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。 具体实现步骤如下:
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。
11.vue和react的区别
1)设计理念不同
- react整体上是函数式编程思想,组件使用jsx语法,all in js,将html与css全都融入javaScript中,jsx语法相对来说更加灵活
- vue的整体思想,是拥抱经典的html(结构)+css(表现)+js(行为)的形式,使用template模板,并提供指令供开发者使用,如v-if、v-show、v-for等,开发时有结构、表现、行为分离的感觉
2)数据是否可变
- vue的思想是响应式的,通过Object.defineproperty或proxy代理实现数据监听,每一个属性添加一个dep对象(用来存储对应的watcher),当属性变化的时候,通知对应的watcher发生改变
- react推崇的是数据不可变,react使用的是浅比较,如果对象和数据的引用地址没有变,react认为该对象没有变化,所以react变化时一般都是新创建一个对象
3)更新渲染方式不同
- 当组件的状态发生变化时,vue是响应式,通过对应的watcher自动找到对应的组件重新渲染
- react需要更新组件时,会重新走渲染的流程,通过从根节点开始遍历,dom diff找到需要变更的节点,更新任务还是很大,需要使用到 Fiber,将大任务分割为多个小任务,可以中断和恢复,不阻塞主进程执行高优先级的任务
4)各自的优势不同
- vue的优势包括:框架内部封装的多,更容易上手,简单的语法及项目创建, 更快的渲染速度和更小的体积
- react的优势包括: react更灵活,更接近原生的js、可操控性强,对于能力强的人,更容易造出更个性化的项目
12.react Hooks
可以在函数式组件中,获取state、refs、生命周期钩子等其他特性
Hook 使用规则
- 只在最顶层使用 Hook,Hooks底层使用链表存储数据,按照定义的useState顺序存储对应的数据,不要在循环、条件或嵌套函数中调用Hook,否则 Hooks的顺序会错乱
- Hook 命名规则:自定义 Hook 必须以 “use” 开头,如useFriendStatus
- Hooks 只能在函数组件中使用,不能在类组件中使用。这是因为 Hooks 是基于函数组件的思想设计的,它利用了函数组件的闭包特性来存储和更新状态
- 在两个组件中使用相同的 Hook 不会共享 state,每次使用自定义 Hook 时,其中的所有state和副作用都是完全隔离的
13.为什么vue和react都选择了Hooks
1. 更好的状态复用
- 对于vue2来说,使用的是mixin进行混入,会造成方法与属性的难以追溯。
- 随着项目的复杂,文件的增多,经常会出现不知道某个变量在哪里引入的,几个文件间来回翻找,
- 同时还会出现同名变量,相互覆盖的情况……😥
2. 更好的代码组织
- vue2的属性是放到data中,方法定义在methods中,修改某一块的业务逻辑时,经常会出现代码间来回跳转的情况,增加开发人员的心智负担
- 使用Hooks后,可以将相同的业务逻辑放到一起,高效而清晰地组织代码
3. 告别this
this有多种绑定方式,存在显示绑定、隐式绑定、默认绑定等多种玩法,里边的坑不是一般的多
vue3的setup函数中不能使用this,不能用挺好,直接避免使用this可能会造成错误的
14.为什么react推行函数式组件
- 函数组件不需要声明类,可以避免大量的譬如extends或者constructor这样的代码
- 函数组件不需要处理 this 指向的问题
- 函数组件更贴近于函数式编程,更加贴近react的原则。使用函数式编程,灵活度更高,更好的代码复用
- 随着Hooks功能的强大,更推动了函数式组件 + Hooks 这对组合的发展
15.useMemo和useCallback的作用与区别
useCallback
返回一个函数,只有在依赖项发生变化的时候才会更新(返回一个新的函数),多用于生成一个防抖函数
注意:组件每次更新时,所有方法都会重新创建,这样之前写的防抖函数就会失效,需要使用useCallback包裹
useMemo
只有在依赖项发生改变的时候,才会重新调用此函数,返回一个新的值, 类似于vue中的computed 计算属性
16.useEffect的用法与作用
- useEffect的用法:需要传入两个参数:副作用函数和依赖数组
- 作用:
处理副作用操作
:在副作用函数内部,可以执行需要在组件渲染时或更新时执行的副作用操作,例如订阅数据、操作DOM、发送请求等控制组件生命周期
:useEffect函数可以模拟类组件的生命周期方法(如componentDidMount、componentDidUpdate和componentWillUnmount)的行为。
17.使用setCount修改数据后,到页面重新渲染,整个流程是怎么样的
初始化状态:使用useState定义一个状态变量和对应的更新函数。例如:const [count, setCount] = useState(0);
执行 setCount(count + 1) 后,到页面重新渲染的整个流程如下:
- 当你调用 setCount(count + 1),React会记录这个更新操作,并将新的值(count + 1)存储在内部。
- React会将组件标记为“脏”(dirty),表示需要重新渲染。
- 组件函数将被再次执行,这一次执行过程中会重新计算组件的UI。在这个过程中,count的值被更新为新的值(count + 1),这将反映在生成的UI中。
- React会将前后两次渲染生成的虚拟DOM树进行比较,找出变化的部分。在这种情况下,count的值发生了变化,因此会更新与count相关的部分。
- 只有变化的部分会被重新渲染,这样可以提高性能。React会将变化的部分转换为实际的DOM操作,更新到页面上。
- 页面更新完成后,用户将看到更新后的页面,其中包含了新的count值的呈现。 总结起来,执行 setCount(count + 1) 后,React会在后续的重新渲染过程中更新组件的UI,使其反映新的状态值。这样,页面就会显示更新后的数据。
17.React hooks解决了什么问题? 函数组件与类组件的区别
函数组件:
- 没有组件实例
- 没有生命周期
- 没有state和setState,只能接受props
- 函数组件是一个纯函数,执行完即销毁,无法储存state
- 需要state hook,即把state功能’钩’到纯函数中
class组件
- 大型组件很难拆分和重构,很难测试(即class不易拆分)
- 相同业务逻辑,分散到各个方法中,逻辑混乱
- 复用逻辑变得复杂,如Mixins、HOC、Render Props
为什么会有react hooks,它解决了什么问题?
- 解决了使用class组件带了一个this指向问题,逻辑与视图强绑定,强耦合的问题
- 解决了逻辑组件复用,一次编写,不同业务组件使用
- hook带来了一个最大的好处就是,原来的那种class组件的编写方式,面对对象编程转化成了函数式编程
18.React组件传值有哪些方式
父传子:props 子传父:通过在父组件引入的子组件中传递一个函数并传参数,子组件去触发这个函数更改参数完成数据更新
跨多层组件传值:通过context api完成
19.React diff 原理
- 把树形结构按照层级分解,只比较同级元素。
- 列表结构的每个单元添加唯一的 key 属性,方便比较。
- React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
- 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty 到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
- 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。
20.hooks
hooks基础
useState、useEffect、useRef、useContext、useReducer、useMemo、useCallback
react hooks如何模拟组件生命周期?
- 模拟componentDidMount useEffect依赖为空
- 模拟componentDidUpdate useEffect无依赖,或者依赖[a,b]
- 模拟componentWillUnMount useEffect中retrun一个函数
react hooks使用规范
- 只能用于顶层代码,不能在循环、判断中使用hooks
- eslint插件eslint-plugin-react-hooks可以规范
hook调用顺序必须保持一致
- 无论是render还是re-render,hooks调用顺序必须一致
- 如果hooks出现在循环、判断里,则无法保证顺序一致
- hooks严重依赖于调用顺序
react hooks性能优化
- 用useMemo缓存数据
- 用useCallback缓存函数
使用react hooks遇见哪些坑?
- useState初始化值,只有第一次有效
- useEffect内部不能修改state
- useEffect可能出现死循环