websocket使用问题
Websocket协议和HTTP协议都位于网络中的应用层,都是应用层协议,而TCP则是位于传输层,属于传输层协议,并且WS和HTTP都是基于TCP实现的上层协议,与HTTP不同的是,WS可以使得客户端(广义客户端,包括浏览器)与服务器建立一个长链接全双工的通信信道,不仅使得客户端可以主动向服务器发送消息,也可以让服务器主动向客户端发送消息,由于是长链接通道所以每次消息的发送并不会反复创建、销毁链接。
WS链接的建立第一步是借助于HTTP协议实现的,后面才是真正的WS链接
注意几个特殊的HTTP Header:
- Connection: Upgrade // 告诉服务器,这个链接要进行协议升级。实际上这个header平时用的更多时候,TA的值是赫赫有名的keep-alive
- Upgrade: websocket // 告诉服务器,具体想升级成websocket协议
- Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits // 协议扩展类型
- Sec-WebSocket-Key: IhTmM/PyVb55uCkAU5Iw1Q== // 传输给服务器的key,这个key的算是这样来的 客户端随机一坨字符,然后base64一下
- Sec-WebSocket-Version: 13 // 客户端支持WebSocket的版本
- 服务器获取到Sec-WebSocket-Key后,在后面加上"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"后然后使用sha1算法生成一个结果,然后再base64一下,当作Sec-Websocket-Accept的值返回给客户端,客户端收到HTTP Response后按照相同的算法也计算出最终base64,和服务器返回的Sec-Websocket-Accept进行对比,如果相同表示验证通过。
websocket安全问题
依据服务端与客户端商议好的口令token进行权限鉴定
https请求之后发生的事
参考:fecommunity.github.io/front-end-i…
三次握手四次挥手
参考:juejin.cn/post/684490…
三次握手的作用:
1、确认双方的接受能力、发送能力是否正常。
2、指定自己的初始化序列号,为后面的可靠传送做准备。
3、如果是 https 协议的话,三次握手这个过程,还会进行数字证书的验证以及加密密钥的生成到。
4.简述 CSRF (跨站请求伪造)的攻击和防御措施(CSDN博客-CSRF)
tech.meituan.com/2018/10/11/… blog.csdn.net/stpeace/art…
5.react的fiber使用
fiber是为了对渲染过程实现更加精细的控制。 Fiber 架构核心:“可中断”“可恢复”与“优先级”。
6.react的合成事件
合成事件是 React 自定义的事件对象,在底层抹平了不同浏览器的差异,在上层面向开发者暴露统一的、稳定的、与 DOM 原生事件相同的事件接口。
自研事件系统使 React 牢牢把握住了事件处理的主动权,例如setState的批量更新,就会在合成事件里开启isBatchingUpdate = true,函数执行完成后再通过事务的close方法把isBatchingUpdate置为false;
虽然合成事件并不是原生 DOM 事件,但它保存了原生 DOM 事件的引用。当你需要访问原生 DOM 事件对象时,可以通过合成事件对象的 e.nativeEvent 属性获取到它
8.webpack5相关知识
10.node的了解
参考:segmentfault.com/a/119000002…
11.react新旧生命周期,用法
react(15-16.3以前) 旧版比较特殊的:
componentWillReceiveProps(newProps)【第一次不会执行,之后无论是否有新props,都会更新】
react(16.4以后) 新版比较特殊的:
static getDerivedStateFromProps(nextProps, nextState) 【将传入的props映射到state上面】 getSnapshotBeforeUpdate()【可以在可能更改之前从DOM捕获一些信息(例如滚动位置),return值】let ThemeContext = React.createContext(null); //父组件 let contextVal = {changeColor: this.changeColor, color: this.state.color}; <ThemeContext.Provider value={contextVal}> <div style={{ margin: '10px', border: `5px solid ${this.state.color}`, padding: '5px', width: 200 }}> page <Header /> <Main /> </div> </ThemeContext.Provider> //子组件 <ThemeContext.Consumer> { (value) => ( <div style={{ border: `5px solid ${value.color}`, margin: '5px', padding: '5px' }}> main <Content /> </div> ) } </ThemeContext.Consumer>
react-hooks(16.8以后)
useAPI
12.页面加载性能优化
参考segmentfault.com/a/119000002…
- react相关的
- 使用 shouldComponentUpdate 规避冗余的更新逻辑
- PureComponent + Immutable.js
- React.memo 与 useMemo,useCallback
- 使用useMemo返回组件代替React.memo
- 网络相关的
- 减少http请求
- 使用服务端渲染
- 静态资源使用cdn或者放在nginx上
- 善用缓存,不重复加载相同的资源
- 图片相关的
- 图片延迟加载(原生支持的懒加载API,非常好用)
- 降低图片质量(jpg比png小,图片质量压缩)
- 使用字体图标 iconfont 代替图片图标(阿里图标库直接生成字体图标)
- 使用svg放大缩小不会失真
- webpack相关的
- 使用url-loader图片转base64
- 使用externals外部引入js库
- js语言相关的
- 将 CSS 放在文件头部,JavaScript 文件放在底部
- 使用switch代替if-else
- 压缩文件
- 避免页面卡顿(fiber切分,定时器切分)
- 使用 requestAnimationFrame 来实现视觉变化
- 使用为操作
- DOM相关的
- 减少重绘回流
- 使用事件委托,节省内存
- css相关的
- 降低 CSS 选择器的复杂性
- 使用 flexbox 而不是较早的布局模型
- 使用 transform 和 opacity 属性更改来实现动画
13.react,map ,key的作用
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。
Diff算法
- Diff 算法性能突破的关键点在于“分层对比”;
- 类型一致的节点才有继续 Diff 的必要性;
- key 属性的设置,可以帮我们尽可能重用同一层级内的节点。
14. 类组件和函数组件的区别(来源:前端面试宝典之react篇)
作为组件而言,类组件与函数组件在使用与呈现上没有任何不同,性能上在现代浏览器中也不会有明显差异。
它们在开发时的心智模型上却存在巨大的差异。类组件是基于面向对象编程的,它主打的是继承、生命周期等核心概念;而函数组件内核是函数式编程,主打的是 immutable、没有副作用、引用透明等特点。
之前,在使用场景上,如果存在需要使用生命周期的组件,那么主推类组件;设计模式上,如果需要使用继承,那么主推类组件。
但现在由于 React Hooks 的推出,生命周期概念的淡出,函数组件可以完全取代类组件。
其次继承并不是组件最佳的设计模式,官方更推崇“组合优于继承”的设计概念,所以类组件在这方面的优势也在淡出。
性能优化上,类组件主要依靠 shouldComponentUpdate 阻断渲染来提升性能,而函数组件依靠 React.memo 缓存渲染结果来提升性能。
从上手程度而言,类组件更容易上手,从未来趋势上看,由于React Hooks 的推出,函数组件成了社区未来主推的方案。
类组件在未来时间切片与并发模式中,由于生命周期带来的复杂度,并不易于优化。而函数组件本身轻量简单,且在 Hooks 的基础上提供了比原先更细粒度的逻辑组织与复用,更能适应 React 的未来发展。
15.为什么需要 React-Hooks
- 告别难以理解的 Class;
- 解决业务逻辑难以拆分的问题;
- 使状态逻辑复用变得简单可行;
- 函数组件从设计思想上来看,更加契合 React 的理念。
16.useState,useMemo,useCallback,useReducer实现
let hookStates = [];
let hookIndex = 0;
// useState实现
function useState(initialState) {
hookStates[hookIndex] =
hookStates[hookIndex] || typeof initialState === 'function' ? initialState() : initialState;
let currentIndex = hookIndex;
function setState(newState) {
hookStates[currentIndex] = newState;
render();
}
return [hookStates[hookIndex++], setState];
}
// useMemo实现
function useMemo(factory, deps) {
if (hookStates[hookIndex]) {
let [lastMemo, lastDeps] = hookStates[hookIndex];
let same = deps.every((item, index) => item === lastDeps[index]);
if (same) {
hookIndex++;
return lastMemo;
} else {
let newMemo = factory();
hookStates[hookIndex++] = [newMemo, deps];
return newMemo;
}
} else {
let newMemo = factory();
hookStates[hookIndex++] = [newMemo, deps];
return newMemo;
}
}
// useCallback实现
function useCallback(callback, deps) {
if (hookStates[hookIndex]) {
let [lastCallback, lastDeps] = hookStates[hookIndex];
let same = deps.every((item, index) => item === lastDeps[index]);
if (same) {
hookIndex++;
return lastCallback;
} else {
hookStates[hookIndex++] = [callback, deps];
return callback;
}
} else {
let newMemo = factory();
hookStates[hookIndex++] = [callback, deps];
return callback;
}
}
// useReducer实现
function useReducer(reducer, initialState, init) {
hookStates[hookIndex] = hookStates[hookIndex] || (init ? init(initialState) : initialState);
let currentIndex = hookIndex;
function dispatch(action) {
hookStates[currentIndex] = reducer ? reducer(hookStates[currentIndex], action) : action;
render();
}
return [hookStates[hookIndex++], dispatch];
}
//useState是一个简化版的useReducer,是一个语法糖
function useState(initialState) {
return useReducer(null, initialState);
}
// useContext实现,useContext和useReducer结合使用
let CounterContext = React.createContext();
export default function App() {
let [state, dispatch] = React.useReducer(reducer, { number: 0 });
let value = { state, dispatch };
return (
<CounterContext.Provider value={value}>
<Counter></Counter>
</CounterContext.Provider>
);
}
function useContext(context) {
return context._currentValue;
}
function Counter() {
let { state, dispatch } = useContext(CounterContext);
return (
<div>
<p>{state.number}</p>
<button onClick={() => dispatch({ type: 'ADD' })}>+</button>
<button onClick={() => dispatch({ type: 'MINUS' })}>-</button>
</div>
);
}
//useEffect实现
function useEffect(callback, deps) {
//会重新渲染
if (hookStates[hookIndex]) {
let lastDeps = hookStates[hookIndex];
let same = deps.every((item, index) => item === lastDeps[index]);
if (same) {
//如果本次的依赖数组和上次的依赖数组一样
hookIndex++;
} else {
hookStates[hookIndex++] = deps;
setTimeout(callback);
}
} else {
hookStates[hookIndex++] = deps;
setTimeout(callback); //模拟一个宏任务,保证callback是在本次页面渲染结束后执行
}
}
function render() {
hookIndex = 0;
ReactDOM.render(<App />, document.querySelector('.root'));
}
17.useMemo独特使用方法
使用useMemo返回函数组件来代替React.memo及useCallback
参考zhuanlan.zhihu.com/p/348796468
import { useEffect, useState, memo, useCallback, useMemo } from 'react';
import ReactDOM from 'react-dom';
import React from 'react';
function Child({ onButtonClick, data }) {
console.log('Child render');
return <button onClick={onButtonClick}>{data.number}</button>;
}
// Child = memo(Child);
function App() {
const [number, setNumber] = useState(0);
const [name, setName] = useState('zhufeng');
const addClick = useCallback(() => setNumber(number + 1), [number]);
const data = useMemo(() => ({ number }), [number]);
let a = useMemo(() => <Child onButtonClick={addClick} data={data}></Child>, [number]);
return (
<div>
<input type="text" value={name} onChange={(e) => setName(e.target.value)} />
{a}
</div>
);
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
18.理解redux
-
Redux 主要由 3 部分组成:Store、Reducer 和 Action。
- Store:它是一个单一的数据源,而且是只读的。
- Action 人如其名,是“动作”的意思,它是对变化的描述。
- Reducer 是一个函数,它负责对变化进行分发和处理,最终将新的数据返回给 Store。
-
主要的API
- getState
- subscribe
- dispatch
- createStore
19.TypeScript使用
范型,接口,枚举(enum)
20.什么是纯函数
/**
纯函数(无副作用)
1.相同的参数返回相同的结果
2.不能修改作用域外的变量
*/
function sum(a,b){return a+b};
function sum2(a,b){window.a='c'; return a+b};
介绍一下HTTP的缓存?(面试重点)
参考:fecommunity.github.io/front-end-i…
www.cnblogs.com/wonyun/p/55…
Http 的缓存主要利用 header 里的Cache-control 和 ETag
- Cache-control
如果给Cache-Control设置了no-cache后,每次要使用资源时浏览器都要到服务器验证缓存是否过期。 如果直接使用缓存,返回码:304- 缓存位置 2.缓存时间 3.重新验证
- ETag
即用来进行对比缓存,Etag 是服务端资源的一个标识码
当客户端发送第一次请求时服务端会下发当前请求资源的标识码 Etag,下次再请求时,客户端则会通过 header 里的 If-None-Match 将这个标识码 Etag 带上,服务端将客户端传来的 Etag 与最新的资源 Etag 做对比,如果一样,则表示资源没有更新,返回 304。 - 缓存方案
-
资源验证
如何验证资源是否过期:Last-Modified(上次修改时间)
配合 If-Modified-Since 或 If-Unmodified-Since 使用 如果请求的资源头中有Last-Modified这个头,这个头指定了一个时间。那么浏览器重新访问资源时就会带上If-Modified-Since这个头,其时间是Last-Modified的时间,服务器就会拿这个时间去对比上次修改的时间,然后告诉浏览器是否可以直接使用。 Etag (数据签名)
资源会依据它的内容产生一个唯一的数据签名,如果资源有更新,那么Etag就会发生变化。 配合 If-Match 或 If-None-Match 使用
项目中遇到的难点
1.antd3和antd4dom结构不同,single-spa外部引入库无法配置主题色。
2.websocket传输数据量太大,导致卡顿,使用spread.js的commend减少传输量。
3.ocr识别和文档中图片,文件太大,使用文件服务,将图片上传到cdn,请求图片地址。