# 传音控股
- Q: React 合成事件(SyntheticEvent)和原生事件有何不同?
点击查看答案
合成事件的背景: 在生成fiber节点时, 其对应的dom节点可能还未挂载, 事件处理函数作为prop也就无法绑定到真实dom上. 采用合成事件就解决了这个问题,同时还可以利用fiber树的结构来模拟事件的捕获与冒泡。
实现机制:
事件委派, 将事件绑定在结构的最外层, 使用统一的事件监听器, 监听器上维护一个保存所有组件上的监听事件和处理函数的映射. 当组件挂/卸载时, 就会在事件监听器上插入或删除一些对象, 事件发生时, 首先事件处理器处理, 然后在事件里找到真正的事件处理函数
自动绑定, 通过this指向当前组件, 通过这种引用方式进行缓存, 从而实现CPU和内存上的最优化. 通过箭头函数则不用
点击查看代码
import React, { Component } from 'react'; class App extends Component { handleClick(e, arg) { console.log(e, arg); } render() { {/* bind */} return <button onClick={this.handleClick.bind(this, 'test')}>Test</button>; {/* arrow function */} <!-- return <button onClick={(e) => this.handleClick(e, 'test')}>Test</button>; --> } }
1
2
3
4
5
6
7
8
9
10
11
12
13
优点:
性能优化, 当dom上绑定过多事件处理函数时, 页面性能以及内存等可能会受到影响, 事件代理可达到性能优化的目的
兼容性, 可以更好的跨平台
统一管理, 对事件进行归类, 设定不同的优先级
- Q: react hooks 对比 class 组件
参考链接
点击查看答案
函数式组件 | class 组件 |
---|---|
维护自己的state/props | 组件实例化一次, 通过 this 拿到最新的state/props |
组件逻辑聚合 | 组件逻辑分散 |
逻辑复用简单 | 逻辑复用困难 |
心智负担小 | 心智负担大(声明周期) |
- Q: Vue(2.x & 3.x版本) diff 算法复杂度以及具体实现
点击查看答案
时间复杂度:
Vue2: O(n)
Vue3: O(nlogn)
2
3
Vue
diff 算法背景: Vue
通过模板编译生成虚拟 DOM
树, 然后通过渲染器渲染成真实 DOM
, 当数据更新时, 产生新的虚拟 DOM
树, 如果直接将新的虚拟 DOM
树渲染成真实 DOM
, 势必造成了性能上的浪费, 为了进一步降低找出差异的性能损耗, 于是就有了 diff 算法(也称对比算法).
# Vue2
采用 双端diff算法
核心方法是 updateChildren
(opens new window), 共5种查找, 对比顺序为: 新前与旧前
-> 新后与旧后
-> 新后与旧前
-> 新前与旧后
-> 暴力对比
在这个查找算法中, 提取了一个公用方法: sameVnode
(opens new window)
function sameVnode (a, b) {
return (
a.key === b.key &&
a.asyncFactory === b.asyncFactory && (
(
a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
) || (
isTrue(a.isAsyncPlaceholder) &&
isUndef(b.asyncFactory.error)
)
)
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 新前(新的开始节点)与旧前(旧的开始节点)
对比相同, 新旧节点开始下标往后移一位, 如果不同, 则进行新后与旧后
对比
- 新后(新的结束节点)与旧后(旧的结束节点)
对比相同, 新旧节点结束下标往前移一位, 如果不同, 则进行 新后与旧前
对比
- 新后(新的结束节点)与旧前(旧的开始节点)
对比相同, 将旧的开始节点移动到老的结束节点后面, 旧的开始下标往后移动一位, 新的结束下标往前移动一位, 如果不同, 则进行 新前与旧后
对比
- 新前(新的开始节点)与旧后(旧的结束节点)
对比相同, 将旧的结束节点移动到旧的开始节点前面, 新的开始下标往后移动一位, 旧的结束下标往前移动一位, 如果不同, 则进行 暴力对比
- 暴力对比(乱序)
遍历 oldChildren
生成 key
和 index
的映射表 {'节点A': 0,'节点B': 1}
, 然后用新的开始节点的 key
在映射表中找, 如果找到就把该节点移动到最前面, 原来的位置用 undefined
占位, 防止数组塌陷, 老节点移动后破坏初始映射表位置. 如果没有找到, 则插入新节点
To be Continued...
- Q: Node.js SSR 渲染流程以及 React 水合
点击查看答案
- Q: Node.js pm2进程管理相关
点击查看答案
- pm2 的特性
进程守护: 守护应用程序进程,确保进程持续运行,即使发生崩溃或意外退出.
自动重启: 如果应用程序崩溃或退出,PM2 可以自动重启进程,以确保应用程序的连续性.
多进程管理: 可以在单个主机上运行多个应用程序实例,以充分利用多核 CPU 的性能优势
日志管理: 配置日志文件的路径、大小限制和日志切割策略
负载均衡: 对于多进程模式下的应用程序, 在不同的进程之间平均分配请求,提高应用程序的并发能力和性能
代码热重载: 在不停止整个应用程序的情况下,重新加载代码更新,以实现零秒部署
集群模式: 将多个应用程序实例分布在多台主机上, 实现高可用性和负载均衡
环境管理: 通过文件配置不同的环境(开发、测试、生产), 在不同的部署环境中使用正确的程序
监控与诊断: 可查看应用程序的CPU使用情况, 内存占用, 进程状态等信息, 用以排查问题和优化性能
- Q: Node.js 部署有哪些痛点
点击查看答案
To be Continued...
- Q: 为什么v-for和v-if不能一起使用
点击查看答案
v-for
比v-if
的优先级更高, 点击查看源码 (opens new window)
export function genElement (el: ASTElement, state: CodegenState): string {
if (el.parent) {
el.pre = el.pre || el.parent.pre
}
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
return genSlot(el, state)
} else {
// component or element
// ...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 如果一起使用, 会造成性能上的浪费, 每次渲染都会先遍历后判断, 一般都是采用
computed
组合使用, 提前过滤掉选项, 或者可以采取 在v-for
外层增加一层容器用v-if
进行判断
- Q: 作为前端的主导负责人, 产品频繁修改需求如何处理
- 从研发的角度, 需求的目的和业务价值
- 产品的角度, 探讨出较为完善的方案
- 项目的角度, 时间人力成本
- 运营的角度, 维护成本增高