前文
准备开一个模拟面试的专栏,就是找网上大厂的面经,然后自己解答作整理,这是第一篇,不过还没完成,现在发出来其实是想变相催更自己。
面试题目来源 反向人:腾讯QQ音乐前端面经(已offer)
还有很多没写完,拖了很久....
一面
react hooks 有哪些优缺点?
优点:
- 组件逻辑更容易复用
- 代码风格更友好,有利于拆分组件,提高组件复用率
缺点:
- 异步代码会导致数据不一致,数据是旧引用的bug
- useEffect的触发依靠响应式,逻辑的执行不够直观
- 成本高
- 复杂组件隐藏副作用难以排查
useLayoutEffect 和 useEffect 区别是什么?
- useLayoutEffect是同步调用,发生在浏览器把内容渲染到界面前
- useEffect是异步调用,发生在浏览器把内容渲染到界面后
说下JSBridge?
JSBridge构建了Native和js进行双向通信的通道,主要目的是给JavaScript提供调用Native的功能。
js调用Native的方式
- Native侧通过JavaScriptInterface注入API供js侧调用, 注入的形式是key-value, key是对象名,value是Native的对象。然后js就可以通过key调用到Native对象
- js侧通过iframe.src或者location.href的方式发送自定义的url scheme,Native通过拦截url scheme进行相应操作。
Native调用js的方式
- 直接调用api执行js拼接的字符串
JSBridge通常需要实现两个函数,
- postMessage js侧调用Native 需要对此次回调进行callBackId的关联,通常是消息队列的形式
- receiveMessage Native侧调用js,携带callBackId和结果,这样js就可以在消息队列找到对应的回调进行执行
通过这种方式,就完成了一次js到native的调用。
说下react-native的原理,原生端和js端是怎么通信的?
react-native 运行了一个带js引擎的线程,线程负责js和原生的通信, 最后通过调用原生渲染从而实现跨平台的能力,react-native的产物是一个js文件,所以可通过更新js文件即可实现热更新。
原生端和js的通信分新旧架构两种方式:
- 旧架构通过bridge,App启动时,原生侧会将原生模块(module)注册到js的映射表,原生模块包括方法(method),即一个module对应多个method的key-value形式。js在调用的时候,通过module + method的方式进行序列化发送到原生,原生解析module得到具体的原生对象,再通过method找到对应的方式,和JSBridge的本质一样,只是做了一层封装。并且,js端自行维护了消息队列实现调用回调。
- 新架构采用JavaScript Interface (JSI),(等待更新.....)
反转单向链表怎么做,要几个指针,都有什么作用?
// https://leetcode-cn.com/problems/reverse-linked-list/
// 要两个指针
// 一个指向前一个节点 一个指向后一个节点
// 遍历结束 pre指向了最后一个节点 返回即可
function reverseList(head: ListNode | null): ListNode | null {
let pre = null
while(head) {
const next = head.next
head.next = pre
pre = head
head = next
}
return pre
};
二面
react中state有层级很深,比如a.b.c.d,如果只更新c属性有哪些办法?immutable.js实现的原理是什么?
// 主要考的是react的immutable, react推崇浅比较
// 对于react的PureComponent或者React.memo而言
// 如果只是修改了b的title,b引用未修改,那么将无法触发组件render
// 深拷贝对象
const oldState = {
a: {
b: {title: "old"},
}
}
const newState = deepClone(oldState)
newState.a.b.title = "new"
// 生成新的b引用
const oldState = {
a: {
b: {title: "old"},
}
}
const newState = {
a:{
b:{
...oldState.a.b,
title: "new"
}
}
}
// 通过immutablejs
// oldState是不可修改的,所以每次修改值之后
// 都会产生新的对象引用
import { Map } from 'immutable';
const oldState = Immutable.Map({
a: {
b: {title:"old"}
}
})
const newState = oldState.set('a', {b:{title:"new"}})
// true
oldState !== newState
对比三个方案
- 深拷贝性能消耗大,而且会导致其他引用失效
- 通过... 生成新的引用,比较啰嗦可读性差
- immutablejs 直观
immutablejs创建的对象无法更改,通过结构共享,每次修改只会影响当前节点其他节点不会改变,通过这种方式产生新的immutablejs对象,避免性能损耗。