题目一(前端社招Zoom一面)
题目来源: www.nowcoder.com/discuss/957…
1. 说说你常用的ES6命令
- const let
- 展开运算符
- 箭头函数
- set map WeakSet WeakMap
- symbol
- bigint
- async await
- promise
- class
- proxy
- reflect
2. 说说Symbol你在平时使用是如何使用的
- 消除魔法字符
- 作为对象属性,避免重复
- 作为类的私有方法,ts有private, es10有#
3. Set转Array有哪些方式
- Array.from() 可转化可迭代对象为数组
- const array = [...set]
- 循环遍历
4. 写一道函数柯里化( sum ( 1 )( 2 )( 3 )( 4 )( 5 )( 6 ) sum ( 1 , 2 )( 3 , 4 )( 5 ) 注意参数个数不确定,不是纯柯里化)
柯里化: 将是可以将 f(a,b,c)
转换为可以被以 f(a)(b)(c)
或 f(a,b)(c)
的形式进行调用
const curry = (fn, n = fn.length, args = []) =>
n === 0
? fn(...args)
: (...args1) => curry(fn, n - args1.length, [...args, ...args1]);
function sum(a, b, c) {
return a + b + c;
}
const currySum = curry(sum);
console.log(currySum(1)(2, 3));
5. 你知道requestAnimationFrame么
requestAnimationFrame浏览器会在下次重绘前执行回调函数,一般用来执行动画。
fps: 浏览器的刷新率一般等于显示器的刷新率,正常60FPS, 浏览器一秒渲染60次,间隔为16.7ms.
我们知道setInterval可以按照时间间隔执行,但是由于是宏任务,执行的时间间隔并不是严格按照我们所设置的,会带来卡顿,体验不好,requestAnimationFrame就是解决这个问题
优点:
- CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,requestAnimationFrame则不会
- 函数节流:按照屏幕帧数渲染,保证流畅的同时不会造成额外渲染
6. 看一道宏任务微任务(其中包含 requestAnimationFrame)
- 宏任务: setTimeout setInterval requestAnimationFrame
- 微任务: Promise.then catch finally 注意点:
- await会执行跟在后面的表达式,下面的代码会放到微任务队列
- 多个script时执行完微任务就会执行下一个script里的代码,等都执行完才会执行第二次宏任务
- requestAnimationFrame的执行时机的是慢于同步任务,但是和宏任务的执行时机是不确定的。多次执行两种结果都有,
setTimeout(function onTimeout() {
console.log("timeout");
requestIdleCallback(function onIdle2() {
console.log("idle2");
});
}, 0);
Promise.resolve().then(function onFulfill1() {
console.log("promise1");
});
requestAnimationFrame(function onAf() {
console.log("raf");
Promise.resolve().then(function onFulfill2() {
console.log("promise2");
});
});
requestIdleCallback(function onIdle1() {
console.log("idle1");
});
// 输出
promise1
raf
promise2
idle1
timeout
idle2
7. React中 Class组件中的this和Hook中的this分别指向什么
class中指组件实例 如点击事件需要绑定this,Hook中是undefined
8. Hook和Class组件分别对应生命周期的情况有哪些
useEffect 相当于 componentDidMount componentDidUpdate componentWillUnmount
// componentDidMount
useEffect(()=>{console.log('第一次渲染时调用')},[])
// componentDidUpdate
useEffect(()=>{console.log('每次更新')})
// componentWillUnmount
useEffect(() => {
return () => {
console.log('组件销毁');
};
});
9. useEffect原理,他用了什么数据结构
数据结构: 单向循环链表
原理: 每个hook会生成生成hook对象,按顺序挂载到hooks链表上,useEffect
以及 useLayoutEffect
比较特殊,会额外生成effect对象,一个单向循环链表
为什么是单向循环
为了实现两个逻辑,一个是新增节点,一个是从头部开始遍历,一般情况我们会生成两个变量记录头结点和尾节点,单向循环可以只用一个变量实现
hooks原理
- 每个hook内部方法都有mountXxx和updateXxx, 会在首次加载和更新时调用, 如useEffect内部叫mountEffect和updateEffect, useRef有mountRef何updateRef,
- 主要是
fiber.memoizedState
和fiber.updateQueue
这两个属性,上面说的mountXxx方法就是生成一个hook对象,挂载到memoizedState上,形成一个单向链表,而有处理副作用的方法useEffect
,useLayoutEffect
会额外生成一个effect
对象,是一个单向循环链表,挂载到updateQueue
上,
为什么是链表,而不是数组或map
- 有个替代方案是限制一个组件调用多次
useState()
, 会导致自定义hook无法使用 - 如果使用key做标识,会有命名冲突
- symbol做标识,会导致同一个hook无法调用两次
- 多重继承问题,多个hook同时调用同一一个hook
...
作者说过key做标识可以实现,但是同样需要遵守额外的规则,而且比遵守Hooks规则的阻力会更大
10. 说一下Fiber和React事件调度机制
React 15 架构
- Reconciler(协调器)—— 负责找出变化的组件;
- Renderer(渲染器)—— 负责将变化的组件渲染到页面上;
React 16 架构
- Scheduler(调度器)—— 调度任务的优先级,expirationTime
- Reconciler(调和器)—— 负责找出变化的组件:更新工作从递归变成了可以中断的循环过程。Reconciler内部采用了Fiber的架构;
- Renderer(渲染器)—— 负责将变化的组件渲染到页面上
React 17 :
- Scheduler(调度器)—— Lane 模型
fiber
之前Reconciler阶段采用递归的方式创建虚拟DOM并提交Dom Mutation,整个过程同步并且无法中断工作或将其拆分为块, 会消耗大量的时间,复杂情况下容易阻塞线程,造成卡顿, fiber就是让Reconciler阶段变成可以打断的,本质是环形链表
React事件调度机制
调度: 利用浏览器提供的queueMicrotask把遍历fiber树这个任务压入浏览器微任务队列等待执行,requestIdleCallback 貌似是个不错的选择,但是它存在兼容和触发不稳定的原因,react17 中采用 MessageChannel 来实现
expiration time
const task;
// 优先级越高,delayTimeByTaskPriority越低
task.expirationTime = MAX_INT_31 - (currentTime + delayTimeByTaskPriority)
- 两种任务队列
- timerQueue: 过期时间大于现在的时间
- taskQueue: 过期时间小于现在的时间 都是小顶堆,最上面就是优先级最高的
缺点: expirationTime
字段耦合了优先级与批次这两个概念,
批处理问题: 在expiration time架构里,有两次更新拥有相同的expirationTime时,这时候它们本来应该同时处理。但实际上因为js是单线程的,这两个更新必须要区分个先后出来, 但如果这两个事件还有联系,会出现不可预测的问题,如异步Suspense问题。
Lane 模型
- 优先级: Lane
- 批次: Lanes,Lanes 是一个整数,该整数所有二进制位为 1 对应的优先级任务都将被执行。例如 Lanes 为 17 时,表示将批量更新 SyncLane(值为 1)和 DefaultLane(值为 16)的任务。类比车道
// 同步事件
export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001;
// 连续触发优先级,例如:滚动事件,拖动事件等
export const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000000010;
export const InputContinuousLane: Lanes = /* */ 0b0000000000000000000000000000100;
插队
高优先级会打断低优先级的任务,假如执行了一半,那么会将Fiber树还原
饥饿问题
低优先级被连续插队后,如果过期了,会将当前lane添加到expiredLanes上,在后续执行该任务的时候使用同步渲染,避免任务饥饿的问题
11. Flutter你有遇到过什么性能或者兼容性的问题么?
12. 算法:括号匹配
实现一个算法,字符串包含"[]" , "()" , "{}",判断是否正确闭合
解法: 栈,先进后出,如果最后栈内长度为0,说明刚好匹配
思路: 栈,先进后出
function brackets(str) {
const arr = [];
const map = {
'{': '}',
'[': ']',
'(': ')'
}
for(let i = 0, l = str.length; i < l; i++) {
let s = str[i];
if(map[s]) {
arr.push(s);
}
else if(map[arr.at(-1)] === s) {
arr.pop();
}
}
return arr.length === 0;
}
题目2(欧科云链)
1. hooks为什么不能放在if里
单向链表顺序存储
2. useContext
const ThemeContext = React.createContext(themes.light);
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
const theme = useContext(ThemeContext);
3. useMemo
缓存计算结果
4. 自定义hook
// 判断是否是首次加载, 比如useEffect首次不加载
export default () => {
const isMountRef = useRef(false);
useEffect(() => {
isMountRef.current = true;
}, []);
return isMountRef.current;
};
// 异步更新下获取最新状态
const useSyncCallback = (callback) => {
const [proxyState, setProxyState] = useState({ current: false });
const Func = useCallback(() => {
setProxyState({ current: true });
}, [proxyState]);
useEffect(() => {
if (proxyState.current === true) setProxyState({ current: false });
}, [proxyState]);
useEffect(() => {
proxyState.current && callback();
});
return Func;
};
5. 数字精度问题
IEEE754标准,数字二进制表示, 0.1是个无限小数,导致误差 0.1 + 0.2 === 0.3 // false 解决方法:
1. 放大后比较
(0.1*1000+0.2*1000)/1000==0.3
2. Number.EPSILON 它表示 1 与大于 1 的最小浮点数之间的差
0.1 + 0.2 - 0.3 < Number.EPSILON // true
6. 检测数据类型
1. typeof
typeof null // object
typeof (()=>{}) // function
2. instanceof
根据原型链查找,如果网页中包含多个框架,会有问题, isArray就是因此出现,使用判断方法是toString
3. consructor
const a = []
a.constructor === Array // true
4. Object.prototype.toString.call
Object.prototype.toString.call('') // [object String]
7. Promise链式调用,以下分别输出什么
1.
Promise.reject(1)
.then((num) => {
console.log(num);
})
.catch((num) => {
return num + 1;
})
.then((num) => {
console.log(num);
});
// 2
2.
Promise.resolve(1)
.then((num) => {
console.log(num);
}).catch((num) => {
return num + 1;
}).then((num) => {
console.log(num);
});
// 1
// undefined
8. 输入框获取焦点边框高亮
input[type=text]:focus, select:focus{
border:1px solid red;
outline:none;
}
9. 列表表头吸顶
stiky 粘性布局 relative和fixed的结合使用
.sticky {
position: sticky;
top: 0;
}
10. 一个父级div,有两个子div,一个固定高度,另一个高度填充满
display: flex
flex-direction: colom
flex: 1
11. 手写算法
// [{ price: 1, size: 2 }, { price: 2, size: 2 }, { price: 1, size: 1 }]] 依次按照price、size降序排序
let arr = [
{ price: 1, size: 2 },
{ price: 2, size: 2 },
{ price: 1, size: 1 },
];
arr.sort((a, b) => {
return a.price === b.price ? b.size - a.size : b.price - a.price;
});
12. 重绘&重排,那些操作可以导致
重绘: 不影响布局 color background visibility border-radius
重排: 影响元素布局 width height display margin padding border
13. 浏览器渲染过程
dom + cssom -> render -> 布局 -> 绘制 -> 渲染层合并
14. component、PureComponent区别
PureComponent
相当于自动帮我们在shouldComponentUpdate对props
和state
进行浅比较
15. 箭头函数this指向
箭头函数this无法被修改
var name = "global";
var obj = {
name: "ngnce",
log: () => {
console.log(this.name);
},
};
obj.log(); // global
16. 哪些样式可以继承
font: font-family font-size
text: text-align line-height
color
visibility
17. e.target e.currentTarget有什么区别
- target:触发事件的元素。
- currentTarget:事件绑定的元素
事件冒泡阶段,
e.currenttarget
和e.target
是不相等的,但是在事件的捕获阶段,e.currenttarget
和e.target
是相等的。
18. 图片懒加载,路由懒加载实现原理是什么
图片懒加载
监听元素是否处于可视范围内,也就是innerHeight是否大于getBoundingClientRect().top,如果大于,赋值src
路由懒加载
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
基于es6的import()动态引入和webpack的代码分割,Webpack编译打包时,会把每个路由组件的代码分割成一个个js文件,初始化时不会加载这些js文件,只当激活路由组件才会去加载对应的js文件。
19. hooks你们实践的经验是什么
setCount(count+1)
setCount((prevCount) => prevCount + 1) // 依赖上一个值
useEffect 注意依赖
useReducer 管理复杂对象的修改
useMemo 缓存计算结果,仅在复杂计算下和传递对象时使用
useCallback 包裹传递给子组件的方法,避免导致子组件重新渲染
20. 手写深拷贝,循环引用问题如何解决
function clonefunc(func) {
let paramReg = /\((.*?)\)/;
let bodyReg = /\{(.*)\}/;
let funcString = func.toString();
if (func.prototype) {
let param = paramReg.exec(funcString);
let body = bodyReg.exec(funcString);
if (body) {
if (param) {
let arrParam = param[1].split(",");
return new Function(...arrParam, body[1]);
} else {
return new Function(body[1]);
}
}
} else {
return eval(funcString);
}
}
function deepclone(obj, hash = new WeakMap()) {
if (hash.get(obj)) return hash.get(obj);
const type = [Date, RegExp, Set, Map]
if (type.includes(obj.constructor)) return obj.constructor(obj);
let allDesc = Object.getOwnPropertyDescriptors(obj);
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);
hash.set(obj, cloneObj);
for (let key of Reflect.ownKeys(obj)) {
if (obj[key] !== null && typeof obj[key] === "object") {
cloneObj[key] = deepclone(obj[key], hash);
} else if (typeof obj[key] === "function") {
cloneObj[key] = clonefunc(obj[key]);
} else {
cloneObj[key] = obj[key];
}
}
return cloneObj;
}
let obj = {
func: function () {
console.log("func");
},
arrow: () => {
console.log("arrow");
},
symbol: Symbol(),
date: new Date(),
array: [1, 2, 3],
};
obj.loop = obj;
newObj = deepclone(obj);
obj.array[0] = 100;
console.log(newObj);
题目3 (蚂蚁金服)
题目来源: juejin.cn/post/695791…
js 有哪些基本类型,说说 typeof 与 instanceof
// 基本类型
string number boolean null undefined symbol bigint
// typeof
typeof null // object
typeof function(){} // function
// instanceof
function myInstanceof(left, right) {
if (typeof left !== "object" || left === null) return false;
let proto = Object.getPrototypeOf(left);
while (true) {
if (proto == null) return false;
if (proto == right.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}
说说new 操作符;
function _new(obj, ...rest) {
// 基于obj的原型创建一个新的对象
const newObj = Object.create(obj.prototype);
// 获取
const result = obj.apply(newObj, rest);
// 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
return result instanceof Object ? result : newObj;
}
什么是 event loop;
事件循环机制, 模拟实现多线程,
- 宏任务: setTimeout setInterval requestAnimationFrame
- 微任务: Promise.then catch finally requestAnimationFrame 是在渲染前执行,与setTimeout的执行顺序不确定
Promise 的用法?了解 allSettled 方法么,怎么实现?
Promise 异步解决方案,三个状态pending、fulfilled、rejected的转换, 状态不可逆。
- race: 只要一个请求完成就返回,不管成功还是失败
- all: 一个失败就结束,
- allSettled: 等待全部执行完,不管成功还是失败
const allSettled = (values) => {
let promises = [].slice.call(values);
return new Promise((resolve, reject) => {
let result = [],
count = 0;
promises.forEach((promise) => {
Promise.resolve(promise)
.then((value) => {
result.push({ status: "fulfilled", value });
})
.catch((err) => {
result.push({ status: "rejected", value: err });
})
.finally(() => {
count++;
if (count === promises.length) {
resolve(result);
}
});
});
});
};
说说闭包;
闭包是指有权访问另一个函数作用域中变量的函数,本质 就是上级作用域内变量的生命周期,因为被下级作用域内引用,而没有被释放。
ES5 实现继承的方法
// 组合寄生式继承
function inherit(children, parent) {
// 创建对象
let prototype = Object.create(parent.prototype);
// 增强对象
prototype.constructor = children;
// 指定对象
children.prototype = prototype;
}
function Parent(name) {
this.name = name;
this.num = [0, 1, 2];
}
Parent.prototype.sayName = function () {
alert(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
inherit(Child, Parent);
Child.prototype.sayAge = function () {
console.log(this.age);
};
说说跨域
浏览器的一种安全限制手段, 域名不同,协议不同都是跨域,
- jsonp
- cors
// 前端
axios.defaults.withCredentials = true
// 服务端
跨域参数 Access-Control-Allow-Origin不能设置为*, 必须设置具体域名
- 服务器代理
- postmessage
- weksocket
commonJS 与 ES6 模块化区别;
- 语法不同,commonjs是module.exports,exports导出,require导入;ES6是export导出,import导入。
- commonjs是运行时加载模块,ES6是在静态编译期间就确定模块的依赖。
- ES6在编译期间会将所有import提升到顶部和function提升,commonjs不会提升require。
- commonjs: 当module.exports的值是字符串、数字等原始类型时,赋值是值拷贝才会产生导出值改变不会导致导入值改变的现象。ES6是导出的一个引用,内部修改可以同步到外部。
- 两者的循环导入的处理不同,commonjs是每个模块都会个默认导出空对象,后续在这个对象上添加导出的属性,es6的导入则是指向被加载模块的引用,需要用户自己保证取值正确
- commonjs中顶层的this指向这个模块本身,而ES6中顶层this指向undefined。
webpack 了解么?loader、plugin 分别是干嘛的?如何实现一个 loader?
- webpack: 打包工具
- loader:转换器
- plugin: 基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务
// 简单的loader
module.exports = function (source) {
return source.replace(/var/g, "const");
};
webpack 如何优化打包速度;
- 忽略解析第三方模块:noParse, 如lodash等不依赖其它模块的库
- ignorePlugin: 例如moment会包含多个语言包,我们可以忽略,并手动导入需要的
- 缓存: cacheDirectory
- 多线程打包: thread-loader
- 多进程压缩
js
:webpack-parallel-uglify-plugin
- babel-loader: 也可以开启缓存
- DLLPlugin: 动态链接库,如vue, react, 只构建一次,后面直接使用
- 开发时开启热更新
说一下 css 盒模型,border-box 和 content-box 区别;
- content-box: 标准盒模型 宽高只包括content
- border-box: 怪异盒模型 宽高包括content, padding, border
说说 BFC;
块级格式上下文,一个独立的区域,让处于BFC内部的元素与外部的元素不会相互影响
约束规则:
- box会在垂直方向排列
- margin塌陷
- 不会与float元素重叠
- 计算高度包括float元素
- 与外界元素互不干扰
触发条件: 根元素 position: absoluted/ fixed display: inline-box / table float overflow不为visible flex grid
应用: margin塌陷的问题, 不属于同一个BFC可以解决 float高度塌陷 自适应两栏布局
移动端响应式布局怎么实现的;
设置视图大小,禁止缩放
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
css单位
- 使用pxToRem转换函数
$rootFontSize: 375 / 10;
// 定义 px 转化为 rem 的函数
@function px2rem ($px) {
@return $px / $rootFontSize + rem;
}
.demo {
width: px2rem(100);
height: px2rem(100);
}
- postcss-pxtorem插件
// input
h1 {
margin: 0 0 20px;
font-size: 32px;
line-height: 1.2;
letter-spacing: 1px;
}
// output
h1 {
margin: 0 0 20px;
font-size: 2rem;
line-height: 1.2;
letter-spacing: 0.0625rem;
}
说一说 flex 布局,有了解 么;
justify-content: flex-start | center | flex-end | space-between | space-around
align-content: flex-start | center | flex-end | space-between | space-around 多行生效,整体
align-items: flex-start | center | flex-end
flex-direction: row | colom 方向
flex-wrap: nowrap | wrap 换行
order 排序
flex: flex-grow | flex-shrink | flex-basis 默认值为 0 1 0
flex:1,等价于flex: 1 1 0%
align-self: auto | flex-start | flex-end | center | baseline | stretch 单个属性
Grid网格布局: 可以方便的在行和列上进行排列
.container {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
}
有兼容 retina 屏幕的经历吗?如何在移动端实现 1 px 的线;
与dpr无关,dpr影响的是精细度。主要原因是设备不支持< 1px的值. 假设设计图是750px,有一条1px的边框,到375px宽度的手机显示,需要缩小一倍,也就是0.5px,然而css里最低只支持1px大小,不足1px就以1px显示。
小数:
.border { border: 0.5px solid #999 }
缺点: ios 8+ 支持, 安卓不支持
伪类+transform
.scale-1px{
position: relative;
border:none;
}
.scale-1px:after{
content: '';
position: absolute;
bottom: 0;
background: #000;
width: 100%;
height: 1px;
transform: scaleY(0.5);
transform-origin: 0 0;
}
说一下 react 组件的生命周期
constructor
static getDerivedStateFromProps
render
componentDidMount
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
componentWillUnmount()
react 组件如何做性能优化,说说 pureComponent;
- key
- useCallback
- useMemo
- Memo
pureComponent: 设置了浅比较的shouComponentUpdate
调用 setState 之后发生了什么;
- React 会为当前节点创建一个更新队列。
- 然后会触发reconciliation 过程,在这个过程中,会使用名为 Fiber 的调度算法,开始生成新的 Fiber 树, Fiber 算法的最大特点是可以做到异步可中断的执行。
- 然后 React Scheduler 会根据优先级高低,先执行优先级高的节点,具体是执行 doWork 方法。
- 在 doWork 方法中,React会执行一遍更新列队中的方法,以获得新的节点。然后对比新旧节点,为老节点打上 更新、插入、替换 等 Tag。当前节点 doWork 完成后,会执行 performUnitOfWork 方法获得新节点,然后再重复上面的过程。
- 当所有节点都 doWork 完成后,会触发 commitRoot 方法,React 进入 commit 阶段。
- 在 commit 阶段中,React 会根据前面为各个节点打的Tag,一次性更新整个 dom 元素。
了解 fiber 么?解决了什么问题?具体原理是;
react之前的更新是同步、不可中断的(使用了递归) ,为了解决这个问题,React提出Fiber架构,意在将更新流程变为异步、可中断的。 将diff过程从递归遍历变成了链表遍历算法。
有用过 hooks 么?怎么看待 hooks?它的原理是;
- 每个hook内部方法都有mountXxx和updateXxx, 会在首次加载和更新时调用, 如useEffect内部叫mountEffect和updateEffect, useRef有mountRef何updateRef,
- 主要是
fiber.memoizedState
和fiber.updateQueue
这两个属性,上面说的mountXxx方法就是生成一个hook对象,挂载到memoizedState上,形成一个单向链表,而有处理副作用的方法useEffect
,useLayoutEffect
会额外生成一个effect
对象,是一个单向循环链表,挂载到updateQueue
上,
了解过 react 最新的一些动态么?time slice 、suspense、server component 能说说么;
Time Slicing 时间分片
判断一帧有空闲时间,则去执行某个任务,目的是为了解决当任务需要长时间占用主进程,导致更高优先级任务(如动画或事件任务),无法及时响应,而带来的页面丢帧(卡死)情况。
requestIdleCallback:
- 实验 api,兼容情况一般。
- 实验结论: requestIdleCallback FPS只有20ms,正常情况下渲染一帧时长控制在16.67ms (1s / 60 = 16.67ms)。该时间是高于页面流畅的诉求。
react选择自己实现:
-
当前帧结束时间: 我们知道requestAnimationFrame的回调被执行的时机是当前帧开始绘制之前。也就是说rafTime是当前帧开始时候的时间,如果按照每一帧执行的时间是16.66ms。那么我们就能算出当前帧结束的时间, frameDeadline = rafTime + 16.66。
-
当前帧剩余时间:当前帧剩余时间 = 当前帧结束时间(frameDeadline) - 当前帧花费的时间。关键是我们怎么知道'当前帧花费的时间',这个是怎么算的,这里就涉及到js事件循环的知识。react中是用MessageChannel实现的。
let frameDeadline; // 当前帧的结束时间
let channel = new MessageChannel();
// 我们可以理解requestAnimationFrame的回调执行是在当前的主线程中,
channel.port2.onmessage = function () {
let timeRema = frameDeadline - performance.now();
if (timeRema > 0) {
// 有空闲时间
}
};
// 计算当前帧的剩余时间
function timeRemaining() {
// 当前帧结束时间 - 当前时间
// 如果结果 > 0 说明当前帧还有剩余时间
return;
}
window.requestIdleCallback = function (callback) {
requestAnimationFrame((rafTime) => {
// 算出当前帧的结束时间 这里按照16.66ms一帧来计算
frameDeadline = rafTime + 16.66;
// 这里发送消息,MessageChannel是一个宏任务,也就是说上面onmessage方法会在当前帧执行完成后才执行
// 这样就可以计算出当前帧的剩余时间了
channel.port1.postMessage("haha"); // 发送内容随便写了
});
};
Suspense
延迟加载组件
// This component is loaded dynamically
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
// Displays <Spinner> until OtherComponent loads
<React.Suspense fallback={<Spinner />}>
<div>
<OtherComponent />
</div>
</React.Suspense>
);
}
server components
就是在服务器上渲染好React组件,前端请求后直接展示。之前我们是基于HTTP的API接口获取数据,现在直接拿到的就是组件+数据。
Server Component具备服务端的能力,比如直接查询DB、访问文件等,通过传送序列化的“指令”来更新客户端组件和视图
什么是 CSRF 攻击,怎么预防;
跨站请求伪造,诱导用户点击已登录的网站,执行危险操作
-
SameSite 可以让Cookie不随着跨域请求发送
- Strict: 只允许相同站点请求的 Cookie\
- Lax: 允许部分第三方请求携带 Cookie\
- None: 都允许
-
Token验证
cookie是发送时自动带上的,而不会主动带上Token,可以在每次发送时主动发送Token 简单token:uid(用户唯一的身份标识) + time(当前时间的时间戳) + sign(签名)。
-
Referer验证, Referer也容易被修改\
-
验证码,效果很好,但是体验较差
为什么说用 css 实现动画比 js 动画性能好;
渲染线程分为main thread(主线程)和compositor thread(合成器线程)。 主线程中维护了一棵Layer树,在compositor thread,维护了同样一颗Layer树,这两棵树的内容是拷贝关系。因此可以彼此不干扰,当Javascript在main thread修改css时,compositor thread可以用自己维护的Layer树做渲染。当Javascript繁忙导致主线程卡住时,合成到屏幕的过程也是流畅的。
CSS的transform属性来实现动画效果,就可以避免重排和重绘,直接在非主线程上执行合成动画操作
什么是 合成层;
在 DOM 树中每个节点都会对应一个渲染对象(RenderObject),当它们的渲染对象处于相同的坐标空间(z 轴空间)时,就会形成一个 RenderLayers,也就是渲染层。渲染层将保证页面元素以正确的顺序堆叠,这时候就会出现层合成(composite),从而正确处理透明元素和重叠元素的显示。
这个模型类似于 Photoshop 的图层模型,在 Photoshop 中,每个设计元素都是一个独立的图层,多个图层以恰当的顺序在 z 轴空间上叠加,最终构成一个完整的设计图。
对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。
http2 与 http1.1 区别;
- 二进制分帧: 1.1响应是文本格式,而2.0把响应划分成了两个帧,图中的HEADERS(首部)和DATA(消息负载)
- 多路复用: 1.1有队头阻塞,必须等其它请求处理完,2.0,可以有任意流,但服务器的荷载是有限的,流控制会帮我们动态调整
- 头部压缩: 1.1 直接传输文本,客户端和服务器共同维护一个对照表,比如1表示get请求,传输的消耗会大大减少, 像user-agent、cookie这种只有首部名称而没有值的首部,第一次传输需要传在静态字典中的索引以及他的值,值会压缩后传输。第二次可以直接传输索引值
- 服务器推送: 浏览器请求index.html,服务器可以把
index.html
、style.css
、example.png
全部发送给浏览器。需要手动配置服务器推送那些内容
说一说 http 缓存;
强缓存
- Expires: Expires= max-age + 请求时间, 修改本地时间可以让缓存失效
- Cache-Control private public no-cache no-store 协商缓存
- Last-Modified: 最后修改时间, 打开文件不修改也算,以秒计,多次短时间内修改无法感知
- ETag: 基于内容的标识,需要服务器额外计算
http 状态码;
- 100 继续 post header请求确认
- 101 切换协议 websocket http2.0
- 200 成功
- 201 请求成功并且服务器创建了新的资源
- 301 永久重定向
- 302 临时重定向
- 303 临时重定向,并使用GET 请求新的 URI
- 304 未修改过 缓存请求返回
- 400 客户端请求错误
- 401 请求未授权
- 403 禁止访问
- 404 找不到
- 500 服务器错误
- 503 暂时无法处理请求,如维护或过载
斐波那契数列
// 递归 时间复杂度O(2^N)
function fibonacci(n) {
if (n == 1 || n == 2) {
return 1;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 缓存 时间复杂度O(n)
const fibonacci = (n) => {
const arr = [1, 1];
for (let i = 2; i < n; i++) {
arr[n] = arr[n - 2] + arr[n - 1];
}
return arr[n];
};