发现面试官的大部分问题来自:https://q.shanyue.tech/
useEffect
和 useLayoutEffect
的区别
useEffect
是异步执行的,执行时机是浏览器渲染完成之后。
useLayoutEffect
是同步执行的,会阻塞浏览器渲染,执行时机是浏览器渲染完成之后,和componentDidMount
等价。有关于DOM
的操作最好放到useLayoutEffect
中去。
拓展:React 生命周期,React 内置钩子。
React.memo
的用途
React.memo(component[, areEqual])
React.memo 是一个高阶组件,将组件通过 React.memo
包装时将会在相同 props
的情况下跳过渲染组件的操作并直接复用最近一次渲染的结果。默认情况下其只会对复杂对象做浅层对比,也可以通过第二个比对函数来进行控制是否更新。
还有什么方法可以实现这个功能
1. 类组件:React.
PureComponent
Pure Components是React Component的一种,它继承了纯函数的特点与优点。在
props
与state
不改变时不会重复渲染。2. 生命周期:shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState):boolean;
这个生命周期用来决定组件是否需要重新渲染,如果返回 true
则说明组件需要重新渲染。React.useCallback
的使用场景
- 和
React.memo
配合使用,优化组件。
- 作为依赖项时,避免高昂的计算和不必要的重复渲染。
React.useState
同步 or 异步,怎么马上获取更新后的值
同步还是异步
在 react17 中,
setState
和 React.useState
并不能保证是同步的,在生命周期函数和合成事件中是异步的,在原生事件监听器和 setTimeout 中是同步的。在 react18 中,是批量异步执行的。拓展:合成事件
- 提供统一的 API,抹平各大浏览器差异
- 所有事件绑定在 React Root Element 进行事件委托
怎么马上获取值
- 使用
useRef
进行缓存
- 使用 自定义 hook 包装回调,类似
setSate
const useStateCallback = (intalVal) => { const [state,setState] = useState(intalVal); const callBackFn = useRef(); const _steState = (newState,callback) => { setState(newState); callBackFn.current = callback; } useEffect(()=>{ if(callBackFn.current) { callBackFn.current(state) } },[state]) return [state,_steState] }
React 中共享全局方法的方式
- 使用 context
const context = useContext(); const Parent = () => { return ( <context.Provider value={{name: 'simon'}}> <Child/> </context.Provider> ) }; const Child = () => { const { name } = useContext(context); return ( <div>{name}</div> ) };
- 在路由嵌套时可以使用 outlet,但本质还是 context
const Parent = () => { return ( <div> <Outlet context={{name:'simon'}}/> </div> ) }; const Child = () => { const {name} = useOutletContext() return ( <div>{name}</div> ) };
HOC
高阶组件是参数为组件,返回值为新组件的函数,是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
高阶组件是组件的包装器,可以为组件注入额外的 props,或拓展组件的生命周期。大多数 HOC 的功能可以使用 自定义 hooks 替代,但 HOC 不用在组件中重复调用,写法有时比较简单。
keep-alive 的实现
keep-alive
是一个抽象组件,它缓存了第一个子元素的 vnode,并为子组件和最近一个非抽象祖先元素建立父子关系。keep-alive 缓存组件名到 this.keys
中,缓存组件到 this.caches
中。在加载组件时分为首次加载和缓存加载,首次加载时会缓存了
vnode
创建生成 DOM 节点,首次渲染时会正常执行生命周期。在缓存加载时,只是更新子组件为缓存组件。
在 React 中怎么缓存路由
使用 react-activation ,用法和
keep-alive
相似。实现原理:由于 React 会卸载掉处于固有组件层级内的组件,所以我们需要将 <KeepAlive> 中的组件,也就是其 children 属性抽取出来,渲染到一个不会被卸载的组件 <Keeper> 内,再使用 DOM 操作将 <Keeper> 内的真实内容移入对应 <KeepAlive>,就可以实现此功能。
React 路由拦截
React-router
中存在 loader
函数,这个函数会在路由渲染前被调用。类似于 vue-router 中的单路由导航守卫,可以在这个函数内进行权限校验和跳转。拓展 :loader 函数的返回结果可以通过useLoaderData
来获取。
React 中怎么做优化
- 使用
React.memo
和React.useCallback
防止组件重复渲染。
- 使用
React.useMemo
或React.useCallback
缓存比较耗性能的复杂计算。
- 使用
React.lazy
和import()
进行组件懒加载和分包。
- 优化加载,多做缓存如 SWR。
- 将耗费性能的运算放在 worker 线程或者使用 wasm。
Axios 和 fetch 的区别
Axios 是对 XHR 的封装库,内部实现了对请求的拦截处理。
XHR 可以获取到上传的进度。
fetch 是浏览器的原生 API,不需要额外引入库,如果要支持 PWA,就必须使用 fetch。
fetch 可以获取到下载的进度。
fetch 可以在 worker 中被监听并缓存。
Function.prototype.call
的函数签名
call([thisArg,arg1,/* …, */ argN]); // 顺便实现一下 Function.prototype.call = function(thisArg,...args) { const func = this; const fnName = Symbol(); thisArg[fnName] = func; const result = thisArg[fnName](..args); delete thisArg[fnName]; return result; }
拓展:applay([thisArg,argArr])
bind(thisArg [, arg1, /* …, */ argN])
函数签名:一个函数签名定义了函数或方法的输入与输出。
flex 属性
flex-grow
flex 主尺寸的增长系数,flex 容器中分配剩余空间的相对比例。剩余空间是 flex 容器的大小减去所有 flex 项的大小加起来的大小。
flex-shrink
空间不够时的压缩比例,默认值为 1,设置为 0 表示不压缩。
flex-basis
表示被分配到的空间,类似于宽度,但比宽度优先。
单值写法
- 一个无单位数 : 它会被当作
flex:<number> 1 0;
<flex-shrink>
的值被假定为 1,然后<flex-basis>
的值被假定为0
。
- 一个有效的 宽度 值:它会被当作
<flex-basis>
的值。
- 关键字
none
,auto
或initial
.
双值写法
第一个值必须为一个无单位数,并且它会被当作
<flex-grow>
的值。第二个值必须为以下之一:- 一个无单位数:它会被当作
<flex-shrink>
的值。
- 一个有效的宽度值:它会被当作
<flex-basis>
的值。
三值写法
- 第一个值必须为一个无单位数,并且它会被当作
<flex-grow>
的值。
- 第二个值必须为一个无单位数,并且它会被当作
<flex-shrink>
的值。
- 第三个值必须为一个有效的宽度值,并且它会被当作
<flex-basis>
的值。
flex: auto;
等效于 flex: 1 1 auto;
flex: initial;
等效于 flex: 0 1 auto;
flex: none;
等效于 flex: 0 0 auto;
Typescript 中如何使用元组作为type的 key
const arr = ['foo','bar','baz'] as const; type Keys = typeof arr[number]; type obj = { [K in Keys]: any }
const 断言会将对象或数组定义为常量,arr 会被定义为只读元组。
readonly['foo','bar','baz'];
先使用 typeof 获取只读元组的的类型,然后再使用 [number] 或 [any] 提取联合类型。
拓展:将对象转为联合类型
const obj = { k1: 'foo', k2: 'bar', k3: 'baz' } as const; type Keys = keyof typeof obj; type Values = typeof obj[keyof typeof obj];
Typescript 如何让属性 a 的值/类型改变时,属性 b 变为可选
使用联合类型
type Preson = { type: 1, name: string } type PresonWithoutName = { type: 2, name?: string } let me: Preson | PresonWithoutName = { type: 2 }
Dapp 唤起浏览器钱包
ethereum .request({ method: 'eth_requestAccounts' })