前端知识整理

231 阅读10分钟

websocket使用问题

  • Websocket协议和HTTP协议都位于网络中的应用层,都是应用层协议,而TCP则是位于传输层,属于传输层协议,并且WS和HTTP都是基于TCP实现的上层协议,与HTTP不同的是,WS可以使得客户端(广义客户端,包括浏览器)与服务器建立一个长链接全双工的通信信道,不仅使得客户端可以主动向服务器发送消息,也可以让服务器主动向客户端发送消息,由于是长链接通道所以每次消息的发送并不会反复创建、销毁链接。

  • WS链接的建立第一步是借助于HTTP协议实现的,后面才是真正的WS链接

  • 注意几个特殊的HTTP Header:

  1. Connection: Upgrade // 告诉服务器,这个链接要进行协议升级。实际上这个header平时用的更多时候,TA的值是赫赫有名的keep-alive
  2. Upgrade: websocket // 告诉服务器,具体想升级成websocket协议
  3. Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits // 协议扩展类型
  4. Sec-WebSocket-Key: IhTmM/PyVb55uCkAU5Iw1Q== // 传输给服务器的key,这个key的算是这样来的 客户端随机一坨字符,然后base64一下
  5. 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相关的
  1. 使用 shouldComponentUpdate 规避冗余的更新逻辑
  2. PureComponent + Immutable.js
  3. React.memo 与 useMemo,useCallback
  4. 使用useMemo返回组件代替React.memo
  • 网络相关的
  1. 减少http请求
  2. 使用服务端渲染
  3. 静态资源使用cdn或者放在nginx上
  4. 善用缓存,不重复加载相同的资源
  • 图片相关的
  1. 图片延迟加载(原生支持的懒加载API,非常好用)
  2. 降低图片质量(jpg比png小,图片质量压缩)
  3. 使用字体图标 iconfont 代替图片图标(阿里图标库直接生成字体图标)
  4. 使用svg放大缩小不会失真
  • webpack相关的
  1. 使用url-loader图片转base64
  2. 使用externals外部引入js库
  • js语言相关的
  1. 将 CSS 放在文件头部,JavaScript 文件放在底部
  2. 使用switch代替if-else
  3. 压缩文件
  4. 避免页面卡顿(fiber切分,定时器切分)
  5. 使用 requestAnimationFrame 来实现视觉变化
  6. 使用为操作
  • DOM相关的
  1. 减少重绘回流
  2. 使用事件委托,节省内存
  • css相关的
  1. 降低 CSS 选择器的复杂性
  2. 使用 flexbox 而不是较早的布局模型
  3. 使用 transform 和 opacity 属性更改来实现动画

13.react,map ,key的作用

Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。

在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。

Diff算法

  1. Diff 算法性能突破的关键点在于“分层对比”;
  2. 类型一致的节点才有继续 Diff 的必要性;
  3. key 属性的设置,可以帮我们尽可能重用同一层级内的节点。

14. 类组件和函数组件的区别(来源:前端面试宝典之react篇)

作为组件而言,类组件与函数组件在使用与呈现上没有任何不同,性能上在现代浏览器中也不会有明显差异。

它们在开发时的心智模型上却存在巨大的差异。类组件是基于面向对象编程的,它主打的是继承、生命周期等核心概念;而函数组件内核是函数式编程,主打的是 immutable、没有副作用、引用透明等特点。

之前,在使用场景上,如果存在需要使用生命周期的组件,那么主推类组件;设计模式上,如果需要使用继承,那么主推类组件。

但现在由于 React Hooks 的推出,生命周期概念的淡出,函数组件可以完全取代类组件。

其次继承并不是组件最佳的设计模式,官方更推崇“组合优于继承”的设计概念,所以类组件在这方面的优势也在淡出。

性能优化上,类组件主要依靠 shouldComponentUpdate 阻断渲染来提升性能,而函数组件依靠 React.memo 缓存渲染结果来提升性能。

从上手程度而言,类组件更容易上手,从未来趋势上看,由于React Hooks 的推出,函数组件成了社区未来主推的方案。

类组件在未来时间切片与并发模式中,由于生命周期带来的复杂度,并不易于优化。而函数组件本身轻量简单,且在 Hooks 的基础上提供了比原先更细粒度的逻辑组织与复用,更能适应 React 的未来发展。

image.png

15.为什么需要 React-Hooks

  1. 告别难以理解的 Class;
  2. 解决业务逻辑难以拆分的问题;
  3. 使状态逻辑复用变得简单可行;
  4. 函数组件从设计思想上来看,更加契合 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。

    1. Store:它是一个单一的数据源,而且是只读的。
    2. Action 人如其名,是“动作”的意思,它是对变化的描述。
    3. Reducer 是一个函数,它负责对变化进行分发和处理,最终将新的数据返回给 Store。
  • 主要的API

    1. getState
    2. subscribe
    3. dispatch
    4. 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
    1. 缓存位置 2.缓存时间 3.重新验证
  • ETag
    即用来进行对比缓存,Etag 是服务端资源的一个标识码
    当客户端发送第一次请求时服务端会下发当前请求资源的标识码 Etag,下次再请求时,客户端则会通过 header 里的 If-None-Match 将这个标识码 Etag 带上,服务端将客户端传来的 Etag 与最新的资源 Etag 做对比,如果一样,则表示资源没有更新,返回 304。
  • 缓存方案

image.png

  • 资源验证
    如何验证资源是否过期:

    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,请求图片地址。