react
react 如何实现keep-alive?
Keep-alive是缓存路由使用的,保留之前路由的状态
实现方法:
React如何进行组件/逻辑复用?
- HOC(高阶组件)
- 属性代理
- 反向继承
- 渲染属性(render props)
- react-hooks
- Mixin (已废弃,不讨论)
JSX本质是什么?
JSX 使用js的形式来写html代码
jsx本身是语法糖,无法直接被浏览器解析,需要通过babel或者typescript来转换成 js。
许多包含预配置的工具,例如:create- react app 和 next.js 在其内部也引入了jsx转换。
旧的 JSX转换会把jsx 转换为 React.createElement调用。
jsx调用js本身的特性来动态创建UI,与于传统模式下的模板语法不同
react中组件通信的几种方式?
react组件之前通讯主要要四种方式
- 父子组件:props,props回调
- 兄弟组件:共同父级,再由父节点转发props,props回调
- 跨级组件:context对象,注入全局变量:getChildContext; 获取全局变量:this.context.color;
- 非嵌套组件:使用事件订阅,向事件对象添加监听器,和触发事件来实现组件之间的通讯,通过引入event模块实现
- 全局状态管理工具:Redux,Mobox维护全局store
react UI组件和容器组件的区别与应用?
容器组件:拥有自己的状态,生命周期
UI组件:只负责页面UI渲染,不具备任何逻辑,功能单一,通常是无状态组件,没有自己的state,生命周期。

react、vue有什么区别?
| 区别 | react | vue |
|---|---|---|
| 模板引擎 | JSX,更多灵活,纯js语法(可以通过babel插件实现模板引擎) | vue template,指令,更加简单 (vue也可以使用jsx语法) |
| 复用 | Mixin->Hoc->render props->hook | Mixin-> slot->Hoc(比较用,模板套模板,奇怪做法)-> vue3.0 function baseed APi |
| 性能优化方式 | 手动优化:PureComponent/shouldComponentUpdate | 自动优化:精确监听数据变化 |
| 监听数据变化的实现原理 | 手动:通过比较引用的方式(diff) | 自动:getter/setter以及一些函数的劫持(当state特别多的时候,当watcher也会很多,导致卡顿) |
| 数据流 | 数据不可变,单向数据流,函数式编程思想 | 数据可变,双向数据绑定,(组件和DOM之间的),响应式设计 |
| 设计思想 | all in js (html->jsx, css->style-component/jss) | html,,css,js写在一个文件中,使用各自的方式 |
| 功能内置 | 少(交给社区,reactDom,propType) | 多(使用方便) |
| 优点 | 大型项目更合适 | 兼容老项目,上手简单 |
数据流对比:
react 数据更改逻辑:

vue数据更改逻辑:

参考资料:
HOC
什么是高阶函数?
如果一个函数,接受一个或多个函数作为参数或者返回一个函数,就可称之为高阶函数
特点:
- 是函数
- 参数是函数
- or 返回是函数
eg: array 对象中的 map,filter,sort方法都是高阶函数
function add(x,y,f){return f(x)+f(y)}let num=add(2,-2,Math.abs)console.log(num);
什么是高阶组件?
高阶组件就是一个函数(react函数组件),接收一个组件,处理后返回的新组件
高阶组件是高阶函数的衍生
核心功能:实现抽象和可重用性
它的函数签名可以用类似hashell的伪代码表示
- W(WrappedComponent)被包裹的组件,当传参数传入hoc函数中
- E(EnhancedComponent)返回的新组件
hocFactory:: W: React.Component => E: React.Component
高阶组件,不是真正意义上的组件,其实是一种模式;
可以对逻辑代码进行抽离,或者添加某个共用方法;
高阶组件是装饰器模式在react中的实现
主要用途:
- 代码重用,逻辑和引导抽象
- 渲染劫持
- 状态抽象和控制
- Props 控制
参考资料:React 中的高阶组件及其应用场景
hoc存在的问题?
一、静态方法丢失
二、refs属性不能透传
三、反向继承不能保证完整的子组件树被解析
hoc高阶组件使用场景?
react-redux :connect就是一个高阶组件,接收一个component,并返回一个新的componet,处理了监听store和后续的处理 ;
react-router :withrouter 为一个组件注入 history对象;
你在项目中怎么使用的高阶组件?
不谈场景的技术都是在耍流氓
- 权限控制://也可以在history路由切换的时候,加个监听方法,在顶层做监听处理,不过页面会闪一下
- 页面级别:withAdminAuth(Page),withVIPAuth(Page)
- 页面元素级别:
- 组件渲染性能追踪
- 页面复用
- 全局常量(通过接口请求),能过hoc抽离 //也可以通过全局状态管理来获取
- 对当前页面的一些事件的默认执行做阻止(比如:阻止app的默认下拉事件)
参考资料:
高阶组件和父组件的区别?
高阶组件可以重写传入组件的state,function,props;可以对代码逻辑进行抽离,重写 ;
父组件只是控制子组件的view层;
hook
hooks原理分析,如何实现?
参考:
hooks与 react 生命周期的关系?
hooks组件有生命周期,函数组件没有生命周期
hooks的生命周期其实就是:
- useState
- useEffect
- useLayoutEffect
//hooks模拟生命周期函数,与class的生命周期有什么区别
react hook优缺点?
react hook是v16.8的新特性;
传统的纯函数组件,
react hooks设计目的,加强版的函数组件,完全不使用‘类’,就能写出一个全功能的组件,不能包含状态,也不支持生命周期), hook在无需修改组件结构的情况下复用状态逻辑;
优势:
- 简洁:react hooks解决了hoc和render props的嵌套问题,更加简洁 (在不使用class的情况下,使用state及react其他特性(省的把纯函数组件/其他组件改来改去))
- 解耦:react hooks可以更方便地把UI和状态分离,做到更彻底的解耦
- 组合:hooks 中可以引用另外的hooks 形成新的hooks, 组合千变万化
函数友好:react hooks为函数组件而生,解决了类组件的几大问题
- 处理了this的指向问题
- 让组件更好的复用(老的class组件冗长、包含自身的状态state)
- 让react编程风格更取向函数式编程风格,使用function代替class
缺点(坑):【useState数组修改】使用useState修改array的值时,不要使用push/pop/splice等直接更改数据对象的方法,否则无法修改,应该使用解构或其他变量代替
- 【hook执行位置】不要在循环、条件 、嵌套中调有hook,必须始终在react函数顶层使用Hook,这是因为react需要利用调用顺序来正确更新相应的状态,以及调用相应的钩子函数,否则会导致调用顺序不一致性,从而产生难以预料到的后果
- 响应式的useEffect: 当逻辑较复杂时,可触发多次
- 状态不同步:函数的运行是独立的,每个函数都有一份独立的作用域。函数的变量是保存在运行时的作用域里面,当我们有异步操作的时候,经常会碰到异步回调的变量引用是之前的,也就是旧的(这里也可以理解成闭包场景可能引用到旧的state、props值),希望输出最新内容的话,可以使用useRef来保存state
- 破坏了pureComponent、react.memo 浅比较的性能优化效果(为了取最新的props和state,每次render都要重新创建事件处函数)
- react.memo 并不能完全替代 shouldComponentUpdate (因为拿不到 state change ,只针对 props change)
参考资料:
hooks中的坑,以及为什么?
hooks注意事项?
- 不同hook中,不要使用判断(if)
react hook底层是基于链表(Array)实现,每次组件被render时,会按顺序执行所有hooks,因为底层是链表,每个hook的next指向下一个hook,所有不能在不同hooks调用中使用判断条件,因为if会导致顺序不正确,从而导致报错
//错误示例function App(){const [name,setName]=useState('xz');//这里不能使用if判断if(configState){const [age,setAage]=useState('0')}}
react hooks异步操作注意事项?
一、如何在组件加载时发起异步任务
二、如何在组件交互时发起异步任务
三、其他陷阱
参考资料:
react hooks API有哪些?
hooks(本质是一类特殊的函数,可以为函数式注入一些特殊的功能)的主要api:
基础Hook:
- useState : 状态钩子,为函数组件提供内部状态
- useEffect :副作用钩子,提供了类似于componentDidMount等生命周期钩子的功能
- useContext :共享钩子,在组件之间共享状态,可以解决react逐层通过props传递数据;
额外的Hook:
- useReducer: action钩子,提供了状态管理,其基本原理是通过用户在页面上发起的action,从而通过reduce方法来改变state,从而实现页面和状态的通信,使用很像redux
- useCallBack:把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的memoized版本,该回调函数仅在某个依赖项改变时才会更新
- useMemo:把””创建””函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时重新计算, 可以作为性能优化的手段。
- useRef:获取组件的实例,返回一个可变的ref对象,返回的ref对象在组件的整个生命周期内保持不变
- useLayoutEffect: 它会在所有DOM变更后同步调用effect
- useDebugValue
demo:
//useMemoconst memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);//useReducerconst [state, dispatch] = useReducer(reducer, initialArg, init);
参考资料: Hook API 索引
useEffect 介绍?
useEffect可以让你在函数组件中执行副使用(数据获取,设置订阅,手动更改React组件中的DOM)操作
默认情况下每次函数加载完,都会执行(不要在此修改state,避免循环调用),useEffect相当于是class组件中的componentDidMount,componentDidUpdate,componentWillUnMount三个函数的组合
参数说明:
- 参数一:
- type:function
- 执行的函数
参数二(可选):监听值
- type:array
- 要监听的值(当监听值改变才执行,如果只想执行一次可以传一个[]):如果值没有改变,就不用执行effect函数,可以传入监听的值
- return callBack: 清理函数,执行有两种情况
- componentWillUnmount
- 在每次userEffect执行前(第二次开始)
- return callBack: 清理函数,执行有两种情况
分类:
一)不需要清除
react更新DOM之后运行的一些额外代码,操作完成即可忽略
使用场景:
- 手动变更DOM(修改title)
- 记录日志
- 发送埋点请求
二)需要清除
effect返回一个函数,在清除时调用 (相当于class中componentWillUnmount生命周期)
由于添加/删除订阅代码的紧密性,所以useEffect设计在同一个地方,如果effect返回一个函数,react将会在执行清除时调用它
使用场景:
- 订阅外部数据源(防止数据泄露)
- 设置页面标题,返回重置原标题
useEffect和useLayoutEffect区别?
- useEffect相比componentDidMount/componentDidUpdate不同之处在于,使用useEffect调度的effect不会阻塞浏览器更新屏幕,这让应用响应更快,大多数据情况下,effect不需要同步地执行,个别情况下(例如测量布局),有单独的useLayoutEffect hook可使用,其API与useEffect相同
- useEffect在副使用结束之后,会延迟一段时间执行,并非同步执行。遇到dom操作,最好使用userLayoutEffect
- userLayoutEffect 里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制前运行完成,阻塞了浏览器的绘制
useContext介绍?
共享状态钩子,在组件之间共享状态,可以解决react 逐层通过props传递数据的问题
使用流程(使用流程和react-redux差不多):
- 创建store:通过 createContext Api
- 包裹整个组件:通过store中的Provider方法
- 注入全局变量,provider组件中
- 引入全局变量: 通过 useContext,传入store的名字,返回一个store对象内容
const { useState, createContext, useContext } = React;function App(){//创建storeconst AppContext=createContext({});function A(){//从store中取值const {name}=useContext(AppContext);return <div>A组件Name:{name}</div>}function B(){//从store中取值const {name}=useContext(AppContext);return <div>B组件Name:{name}</div>}//在顶层包裹所有元素,注入到每个子组件中return (<AppContext.Provider value={{name:'xz'}}><A/><B/></AppContext.Provider>)}ReactDOM.render(<App />,document.getElementById('app'));
参考资料:
useReducer介绍?
action 钩子,提供了状态管理
实现过程(和redux差不多,但无法提供中间件等功能 ):
- 用户在页面上发起action (通过dispath方法)
- 从而通过reducer方法来改变state,从而实现 页面和状态的通信
如何创建自己的hooks?
实现步骤:
- 定义一个 hook函数,并返回一个数组(内部可以调用react其他hooks)
- 从自定义的hook函数中取出对象的数据,做业务逻辑处理即可
useCallBack介绍?
useState介绍?
useState 是一个内置的 React Hook。useState(0) 返回一个元组,其中第一个参数count是计数器的当前状态,setCounter 提供更新计数器状态的方法。
...const [count, setCounter] = useState(0);const [moreStuff, setMoreStuff] = useState(...);...const setCount = () => {setCounter(count + 1);setMoreStuff(...);...};
Fiber
什么是Fiber?是为了解决什么问题?
react fiber 是一种基于浏览器的单线程调度算法
react算法的改变(Fiber实现的原理):
- 16版本之前: reconcilation 算法实际上是递归,想要中断递归是很困难的
- 16版本之后:开始使用循环来代替之前的递归
fiber: 一种将 recocilation(递归diff),拆分成无数据个小任务的算法;它随时能够停止,恢复。停止恢复的时机取决于当前的一帧(16ms)内,还有没有足够的时间允许计算
fiber是react16中新发布的特性;
解决的问题:
react在渲染过程时,从setState开始到渲染完成,中间过程是同步;
如果渲染的组件比较大,js执行会长时间占有主线程,会导致页面响应度变差,使得react在动画,手势等应用中效果比较差;
实现过程及原理(核心理念就是:time slicing):
- 拆分:把渲染过程进行拆分成多个小任务
- 检查:每次执行完一个小任务,就去对列中检查是否有新的响应需要处理
- 继续执行:如果有就执行优化及更高的响应事件,如果没有继续执行后续任务
refs
react的refs有什么用,使用场景?
react 主要提供了一种标准数据流的方式来更新视图;
但是页面某些场景是脱离数据流的,这个时候就可以使用 refs;
react refs 是用来获组件引用的,取后可以调用dom的方法;
使用场景
- 管理焦点、选择文本或者媒体播放时,获取焦点 this,refs.inputPhone.focus();
- 触发式动画
- 与第三方DOM库整合
refs 注意事项:
不能在无状态组件中使用refs
