🌞Moon Will Know
🏝️

2022.10.21 面试记录

发现面试官的大部分问题来自:https://q.shanyue.tech/

useEffectuseLayoutEffect 的区别

  • 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的一种,它继承了纯函数的特点与优点。在 propsstate 不改变时不会重复渲染。

2. 生命周期:shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState):boolean; 这个生命周期用来决定组件是否需要重新渲染,如果返回 true 则说明组件需要重新渲染。

React.useCallback 的使用场景

  • React.memo 配合使用,优化组件。
  • 作为依赖项时,避免高昂的计算和不必要的重复渲染。

React.useState 同步 or 异步,怎么马上获取更新后的值

同步还是异步

在 react17 中,setStateReact.useState 并不能保证是同步的,在生命周期函数和合成事件中是异步的,在原生事件监听器和 setTimeout 中是同步的。在 react18 中,是批量异步执行的。
拓展:合成事件
  1. 提供统一的 API,抹平各大浏览器差异
  1. 所有事件绑定在 React Root Element 进行事件委托

怎么马上获取值

  1. 使用 useRef 进行缓存
  1. 使用 自定义 hook 包装回调,类似 setSate
    1. 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 中共享全局方法的方式

  1. 使用 context
    1. const context = useContext(); const Parent = () => { return ( <context.Provider value={{name: 'simon'}}> <Child/> </context.Provider> ) }; const Child = () => { const { name } = useContext(context); return ( <div>{name}</div> ) };
  1. 在路由嵌套时可以使用 outlet,但本质还是 context
    1. 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 中怎么做优化

  1. 使用 React.memoReact.useCallback 防止组件重复渲染。
  1. 使用 React.useMemoReact.useCallback 缓存比较耗性能的复杂计算。
  1. 使用 React.lazyimport() 进行组件懒加载和分包。
  1. 优化加载,多做缓存如 SWR。
  1. 将耗费性能的运算放在 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> 的值。
  • 关键字noneautoinitial.

双值写法

第一个值必须为一个无单位数,并且它会被当作 <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' })