前端知识点总结
浏览器渲染机制简要机制
浏览器采用流式布局模型(Flow Based Layout)
浏览器会把 HTML 解析成 DOM,把 CSS 解析成 CSSOM,DOM 和 CSSOM 合并就 产生了渲染树(Render Tree)。
有了 RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大 小和位置,最后把节点绘制到页面上。
由于浏览器使用流式布局,对 Render Tree 的计算通常只需要遍历一次就可以完 成,但 table 及其内部元素除外,他们可能需要多次计算,通常要花 3 倍于同 等元素的时间,这也是为什么要避免使用 table 布局的原因之一。
重排
由于节点的几何属性发生改变或者由于样式发生改变而不会影响布局的,称为 重绘,例如 outline, visibility, color、background-color 等,重绘的代价是高昂的, 因为浏览器必须验证 DOM 树上其他节点元素的可见性
回流
因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的回流可能会导致了其所有子元素以及 DOM 中紧随其后的节点、祖先节点元素的随后的回流。
1 | <body class="error"> |
在上面的 HTML 片段中,对该段落(
标签)回流将会引发强烈的回流,因为它 是一个子节点。这也导致了祖先的回流(div.error 和 body – 视浏览器而定)。
此外,<h5>和<ol>也会有简单的回流,因为其在 DOM 中在回流元素之后。大部 分的回流将导致页面的重新渲染。 回流必定会发生重绘,重绘不一定会引发回流。
主要包括以下属性或方法: 1、offsetTop、offsetLeft、offsetWidth、offsetHeight 2、scrollTop、scrollLeft、scrollWidth、scrollHeight 3、clientTop、clientLeft、clientWidth、clientHeight
4、width、height
5、getComputedStyle()
6、getBoundingClientRect() 所以,我们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。
script async vs defer
原文:(https://github.com/xiaoyu2er/blog/issues/8)
两者都会并行下载,不会影响页面的解析。
defer 会按照顺序在 DOMContentLoaded 前按照页面出现顺序依次执行。
async 则是下载完立即执行。
- 普通的JS加载script标签浏览器会做如下处理
1
<script src="a.js"></script>
停止解析 document.
请求 a.js
执行 a.js 中的脚本
继续解析 document
- 使用defer
1
2<script src="d.js" defer></script>
<script src="e.js" defer></script>
- 不阻止解析 document, 并行下载 d.js, e.js
- 即使下载完 d.js, e.js 仍继续解析 document
- 按照页面中出现的顺序,在其他同步脚本执行后,DOMContentLoaded 事件前 依次执行 d.js, e.js。
- 使用async
1
2<script src="b.js" async></script>
<script src="c.js" async></script>
- 不阻止解析 document, 并行下载 b.js, c.js
- 当脚本下载完后立即执行。(两者执行顺序不确定,执行阶段不确定,可能在 DOMContentLoaded 事件前或者后 )
事件循环机制
附件:面试真题
OPPO
- CSS flex布局对角线布局
- HTTPS详解
- 内盒子margin top 500,外盒子margin top 200
- weakmap vs map
- 实现hashmap方式
- 重点了解FLEX布局,GRID布局
- vue的模板编译原理,vueloader如何文件编译
- VUE响应式的原理
- 实现一个简易的promise(实现原理)
富途
- HTML XSS攻击 CSRF攻击,服务端防御攻击
- vue 数组双向绑定
- 算法:括号匹配、主串子串match
- vue nextTick实现原理
- 浏览器缓存,协商缓存,强缓存
- nodejs用过么
- HTTP2.0 / HTTP比较
萌时科技
- taro实现原理
- generator使用
function * generator() {
const a = yield 1;
console.log(‘a’, a)
} - async/await的es5实现
- NGINX负载均衡算法
- 性能优化
export function shallowCopy
// show me the code
}
Q1: 实现一个非原生类的浅拷贝函数
// 例
export function testShallowCopy() {
class UserModel {
firstName: string;
lastName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
fullName() {
return this.firstName + " " + this.lastName;
}
}
const user = new UserModel(“Hello”, “World”);
const user2 = shallowCopy(user);
user2.firstName = “Hello 2”;
user2.lastName = “World 2”;
console.assert(user2 instanceof UserModel, “should be instance”);
console.assert(
user.fullName() === “Hello World”,
“should keep original fields”
);
console.assert(
user2.fullName() === “Hello 2 World 2”,
“should returns new value”
);
}
Q2: 实现一个加权随机函数
// 函数 createWeightedSampling 接收一个整数数组 arr, 此数组元素个数
// N < 10000, 元素的值大于 0 且小于 100. 返回一个随机函数, 此随机
// 函数返回 [0, N - 1] 之间的一个随机整数. 每个整数 i 被返回的概率为
// 数组 arr 的第 i 个元素的值 / 数组 arr 的所有元素之和.
//
// 例: 给定一个数组 input, 值为 [4, 2, 1, 3],
// 调用 createWeightedSampling(input),
// 返回一个函数, 此函数返回一个 0 - 3 之间的一个随机整数, 相应的概率
// 分别为 4/10, 2/10, 1/10, 3/10.
//
// 在保证此随机函数的时间复杂度为 O(1) 的前提下, 尽可能优化其性能
export function createWeightedSampling(input: number[]): () => number {
// show me the code
}
// 例:
export function testWeightedSamping() {
const input = [4, 2, 1, 3];
const sampling = createWeightedSampling(input);
const count = [0, 0, 0, 0];
for (let i = 0; i < 10000; i++) {
count[sampling()]++;
}
const rates = count.map((i) => Math.round(i / 1000));
console.assert(
JSON.stringify(rates) === “[4,2,1,3]”,
“should same to snapshot”
);
}
Q3: 实现一个异步任务执行器
// 其最多只能同时执行 capacity 个异步任务, 若同时执行任务数达到 capacity, 则需要等待已有任务完成后才能被执行.
export class AsyncWorker {
constructor(readonly capacity: number) {}
exec
// show me the code
}
}
// 例:
export async function testAsyncWorker() {
const start = Date.now();
const createTask = (timeout: number, error?: any) => {
return () =>
new Promise
setTimeout(() => {
const realCost = Date.now() - start;
const idealCost = (realCost - (realCost % 100)) | 0;
console.log(TASK DONE, timeout: ${timeout}, cost: ${idealCost}
);
if (error) reject(error);
else resolve(timeout);
}, timeout);
});
};
const worker = new AsyncWorker(2);
await Promise.allSettled([
worker.exec(createTask(100)),
worker.exec(createTask(201)),
worker.exec(createTask(202, “FAKE ERROR”)),
worker.exec(createTask(203)),
worker.exec(createTask(300))
]);
const r = await worker.exec(createTask(100));
console.log(“RETURN”, r);
// 期望控制台得到以下输出:
// TASK DONE, timeout: 100, cost: 100
// TASK DONE, timeout: 201, cost: 200
// TASK DONE, timeout: 202, cost: 300
// TASK DONE, timeout: 203, cost: 400
// TASK DONE, timeout: 300, cost: 600
// TASK DONE, timeout: 100, cost: 700
// RETURN 100
}
Temi后端面试:
- NGINX负载均衡算法
- 正向代理和反向代理区别
- 事件驱动模型
- VUE中的事件驱动
- 消息队列类型,已经原理
字节一面:
CDN部署,节点
事件循环机制
浏览器事件处理机制
HTTPS,HTTP 2与HTTP 1区别
XSS攻击原理及防范方式
CRSF原理及防范方式
性能优化的手段
requestAnimation的作用说明
简单请求和复杂请求区别,并举例
小程序catchtap和bindtap区别
事件捕获与事件冒泡
react子组件调用父组件方法,父组件调用子组件方法
webpack plugin与loader区别
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 miaozixiong@gmail.com