2024-12-06|
前端面试HTMLCSSJavaScriptReact

前端面试笔记

汇总 HTML、CSS、JavaScript、框架、网络和工程化等前端面试知识与问答。

面试笔记

面试笔记

HTMl

HTML语义化

  • 语义化标签是一种写HTML标签的方法论,明确了html的书写规范

  • 实现方法是,通过使用语义化的标签名,例如 标题:h1-h6,段落:p, 文章:article,内容:main, 边栏:aside,导航:nav

  • 优点

    • 利于seo,适合搜索引擎搜索

    • 适合人类阅读,利于团队维护

HTML5 有哪些新增的标签

  • 文章相关

    • header,main,footer,nav,section,article,figure,mark
  • 多媒体

    • video, audio, svg canvas
  • 表单

    • type=email type=tel

Canvas 和 SVG 的区别

  • Canvas 主要使用笔刷来绘制2D 图形的

  • SVG主要通过标签来绘制不规则矢量图的

  • 相同:都是主要用来绘制2D图形的

  • 不同点

    • svg 节点过多渲染慢,canvas 性能好一些,但是写起来复杂

    • svg支持分层和事件,canvas不支持,但是可以使用库实现

DOM 事件模型

  • 先经历从上到下的捕获阶段,再经历从下到上冒泡阶段

  • addEventListener 的第三个参数可以选择阶段 可以使用 event.stopPropagation() 来阻止捕获或者冒泡

SEO

  • 内部优化

    • 合理的title, description keywords

      • 一般不超过80个字符, 词语之间英文‘-’隔开
      • 一般不超过150个字
      • 一般三个关键字
  • 代码语义化

  • 非装饰性的图片必须➕alt

  • 对于不现实的内容,谨慎使用display:none

  • 重要内容放在html 前面

  • 少使用iframe

    • 搜索引擎抓取不到iframe 的内容
  • 外部优化

    • 在各个搜索网站提交自己的网站。
  • 微格式

    替换元素和非替换元素

    • 替换元素(内容不由css控制)

      • iframe, img, audio, vadio,

    页面可见性(是否正在看当前页面)

    • document.hidden, 是否为当前用户观看的页面

    • document.visibilityState

      • visibile: 标签页面为浏览器当前激活tab 而且窗口不是最小化状态

      • hidden:表示页面不是被激活的tab, 或者窗口最小化了

      • prerender : 表示页面正在重新生成,对用户不可见

    • visibilitychange 事件

      • 监听 document.visibilityState 变化的

    CSS

    CSS 中的单位有哪些

    • 绝对单位

      • cm

      • mm

      • in

      • pc

      • pt

      • px

    • 相对单位

      • em

        • 在font-size 中使用相对于父元素的字体大小
      • ex

        • 字符‘x’高度
      • ch

        • 数字0的宽度
      • rem

        • 根元素的字体大小
      • lh

        • 元素的line-height
      • vw

        • 视窗的宽度
      • vh

        • 视窗的高度
      • wmin

        • 视窗宽高取最小值
      • wmax

        • 视窗取最大的一条边

    BFC 是什么

    • 是块级格式化上下文

    • 页面上的一块渲染区域, 有一套属于自己的渲染规则, 它决定了元素如何对齐内容,进行布局 以及与其他元素的关系和相互 当涉及到可视化布局的时候,BFC提供一个环境,HTML 按照一定的规则进行布局 body 是一个天然的BFC

    • 规则

      • 内部的盒子会在垂直方向一个接着一个的放置

      • box 垂直方向距离由margin 决定, 属于同一个BFC的相邻的box 的margin 会发生重叠

      • 每个盒子的左外边框紧挨着包含快的左边框,即使浮动元素也如此

      • BFC区域不会与浮动Box 重叠

      • BFC 就是页面上的一个隔离的单独容器,在容器里面的子元素不会影响外面元素

      • 计算BFC的高度时 浮动元子元素也参与计算

    • 触发BFC的条件

      • 根元素

      • float

        • left, right
      • position 绝对定位

        • absolute , fixed
      • overflow 不为 visible 的块元素

        • auto , scroll hidden
      • display

        • inline-block table-cell
      • flex

    • 解决了什么问题

      • 清除浮动

      • 防止垂直方向的margin 合并

      • 高度坍塌

    • 参考答案

      • BFC指的是一个独立的布局环境, BFC 内部元素布局与外界互不影响 触发BFC的方式有很多例如
    1. 设置浮动
    2. overflow: auto scroll hidden
    3. position: absolute/fixed

    常见bfc 的应用

    1. 解决浮动导致父元素高度坍塌的问题
    2. 解决非浮动元素被浮动元素覆盖的问题
    3. 解决外边距垂直方向重合的问题
    • 什么是IFC, GFC, FFC

      • IFC: 行内格式化上下文: 将一块区域按照航煤元素的形式来格式化

      • GFC: 网格布局格式化上下文, 将一块区域按网格的形式来格式化

      • FFC: 弹性格式化上下文, 将一块区域以弹性盒的形式格式化

    如何实现垂直居中

    • table 自带功能

    • 将元素装为table(display:table) 子元素display: table-cal

    • margin-top -50%

    • translate-y -50%

    • position:absolute ; margin:auto;

    • flex布局

    • 100%高度的after before 加上 inline block

    隐藏元素的方法有哪写

    • 完全隐藏

      • 元素从渲染数上消失

        • display: none

    • 视觉隐藏

      • 屏幕无可见,但是占据空间

        • opacity: 0

        • visibility:hidden

        • position: absolute left: -999px top:-999px

        • margin-left:-999px(将元素移出视觉范围)

        • 裁剪:

    • 语义隐藏

    浮动

    • 出现背景

      • 出现浮动是为了做文字环绕效果,让文字环绕图片,从而使网页实现类似word 中图文混排的效果
    • 规则

      • 浮动元素会脱离文档流

      • 浮动元素会互相贴靠,宽度不够时会重启一行

    • 清除浮动

      • 给父元素设置高度(高度要大于子元素的高度)

      • clear 属性

        • 缺点: margin 失效
      • 隔墙法(在两个浮动元素中间添加一个空dev 并添加clear: both)

        • 缺点: 增加了多余的节点
      • 内墙法: 在浮动元素的内部添加一个子颜色,并添加clear: both

      • 伪元素法: 将浮动元素添加伪元素,并设置clear:both 属性

      • overflow:hidden(BFC)

    定位

    • position 属性

      • static(静态定位)

        • 就是指标准流, 在标准流中,块级元素独占一行,内嵌元素共享一行,静态定位是html 的默认值,静态定位元素不会受到 top,bottom,left right
      • relative(相对定位)

        • 所谓相对定位就是相对于自己原来的位置进行定位,相对定位最大的特点就是不会脱离标准流,相对于自己原来的位置上进行偏移

          • 作用,:微调元素 做绝对定位的参考值,子绝父相
      • absolute(绝对定位)

        • 相对于离自己最近,定了位的元素 ,进行偏移,

          • 如果没有定位元素,会参考document 进行定位,

            • top: 文档的左上角

            • bottom: 浏览器窗口首次刷新的底部位置进行定位

          • 如何有设置元素,定位是位置是元素的左上角(在border 里面 )

        • 谁用绝对定位的元素会脱离标准流,

        • 绝对定位的盒子会变成block

        • margin 会失效

      • fixed 固定定位

        • 可以看作是一个特殊的绝对定位, 定位元素也会脱离文档流

        • 固定定位的特殊点是相对于浏览器窗口进行定位

      • sticky 粘性定位 css3

        • 固定定位和相对定位的混合, 元素在跨越特定阀值前为相对定位,之后为固定定位。

    css 属性的计算过程

    • 大致过程 顺序

      • 确定声明值

        • 代码中写的css ,就是声明的内容
      • 层叠冲突

        • 当代码中粗线多个地方对同一个元素的同一个属性作用时,就会出现层叠冲突,此时会根据选择器的权重值来计算,使用那个属性值
      • 使用继承

        • 如果该条属性没有被作者设置,那么会去看祖级元素是否有可以继承的值
      • 使用默认值

        • 最终作者没有设置样式的属性,会使用浏览器的默认样式

    css 的层叠继承规则总结

    • 层叠

      • 重要性

        • 作者样式表优先默认样式表
      • 专用性 (选择器权重)

        • 行内样式 > ID > class> 标签
      • 源代码次序

        • 权重值一样,后声明的覆盖先声明的

    css 引入的方式有哪些

    • 外联: link 标签

    • 内联: style 标签

    • 内嵌: @import

      • @import url("xxx.css")

      • 支持媒体查询 @import url("xxx.css") print

      • 和 link 的区别

          1. link 是标签 , import 是css 的一种方式
        • 2.. 加载顺序的差别 在a.css中使用import b.css时,只有当a.css 下载,解析后,浏览器才会知道b.css 需要下载,这时才回下载 b.css,然后解析,构建render tree

          1. import 是css2.1 提出的,有兼容性问题
          1. 当使用js 控制DOM 去改变样式时,只能通过link 标签,import 不是dom ,无法被控制

    calc 计算属性(css3 的新增功能)

    • 它可以计算出任何的时间 长度, 数值,角度,频率

    css 媒体查询

    • 媒体查询的方式

        1. link
        1. xml
        1. @import url("") screen
        1. @media css3
        • @media screen { // style code }

    css 过渡属性 transition

    • transition 是复合属性

      • transition-property

        • 需要设置的过渡属性
      • transitiion-duration

        • 过渡需要的时间
      • transition-timing-function

        • 过渡函数
      • transition-delay

        • 过渡的延迟时间
    • 可以通过 ontransitionend 监听过渡效果的完成

    css 动画

    • animation : 动画名 时间;

    • 动画声明: @keyframes 动画名 { 0%{} 50%{} 100%{} }

    css 优先级 如何确定

    • 选择器越具体优先级越高, 相同有优先级,后出现的,覆盖前面的。 属性后面加 !important 优先级最高,但是要少用

    css 如何清除浮动

    • 给父元素增加 overflow:hidden (触发BFC)

    • 给父元素增加伪类: parent:afert:{ content: ''; display: block; 或者table clear:both; }

    • tips: 现在不使用 浮动布局,以前IE 不支持 flex 布局才会有浮动布局的。

    盒模型 box-sizing

    • content-box(firefox )

      • width指的是 content区域的宽度,而不是实现宽度

        • 实际宽度 = width+ padding+ border
    • border-box(IE 盒模型)

      • wdith 指的是 左右padding +左右border
    • 相同点都是指定宽度的,不同点 border-box更好用,计算方便

    JS

    数据类型

    • 变量声明

      • var

        • 没有块级作用域

        • 有全局作用域和函数作用域的概念

        • 不初始化默认值为 undefined

        • 存在变量提升

        • 全局作用域用var 声明的变量会挂载到Window对象上

        • 统一作用域允许重复声明

      • let

        • 有块级作用域概念

        • 不存在变量提升

        • 有暂时性死区

        • 不存在全局作用域概念

        • 同一块作用域不允许重复声明

      • const

        • 和 let 相同只有两点差别

          • 必须立即初始化,

          • 不能重复赋值

    • JS 数据类型有哪些

      • Null

      • Undefind

      • Boolean

      • String

      • Number

        • 是双精度浮点数
      • Object

      • Bigint

      • Symbol

    • 如何判断数据类型

      • typeof 判断 基础数据类型 返回小些的类型名

      • instanceof 判断由object 生成的数据类型

      • Object.prototype.toString.call(value) /// [object Type]

    • 浮点数精度问题 0.1+0.2 === 0.3000000004

      • 导致的原因

      • js中的数字都是以64 为浮点数存储的,

      • 精度丢失的原因

        • 浮点数表示是用IEEE 754 标准, 符号位 + 指数位 + 尾数位

          • 符号位: 第一位表示正负符号, 1表示负数,0表示正数

          • 指数位: 中间 十一位存储指数, 用户来表示次方数

          • 尾数位置: 最后52位,存储小数部分,超出部分自动进一舍零

      • 解决方案:

        • math.js

        • decimal.js

        • big.js

    • 原型和原型链的概念

      • 原型

        • 每个 JavaScript 函数都有一个原型属性(prototype),这个属性是一个对象,它包含了可以由该函数创建的所有实例共享的属性和方法。 当创建一个函数时,该函数的 prototype 属性会自动获得一个 constructor 属性,指向函数自身。 通过原型,可以实现属性和方法的继承
      • 原型链解决了什么问题

        • 在es6之前,没有class 的情况下,实现继承

          • a = new Array() a 是 Array 的实例 a 拥有Array.prototype里的属性 (a 通过 a.proto 获取到Array.prototype) Array 继承了Object a 是Object 的间接实例,a 也拥有Object.prototype 里的实例 (通过a.proto.proto 获取到的Object.prototype)
        • 缺点不支持私有属性(js 创造的时候忘记加私有属性了)

    • this 是什么

      • 它是 call 的第一个参数 例如 a={ fn(){} }

    a.fn() ==>a.fn.call(a) fn 中的this 就是 a

    • new 做了些什么

      • 创建临时对象

      • 绑定原型

      • 将this 指定到 临时对象

      • 执行构造函数

      • 返回临时对象

      • [Summary] function myNew(Constructor, ...args) {

      // 等价于: let obj = {}; obj.proto = Constructor.prototype; const obj = Object.create(Constructor.prototype);

      // 2. 绑定 this 并执行构造函数 // 如果构造函数有返回值,result 会接收到它 const result = Constructor.apply(obj, args);

      // 3. 处理返回值 // 如果构造函数返回了一个对象(比如 {}、new Date()、new Map()等), // 则返回该对象;否则返回我们最开始创建的那个对象。 const isObject = typeof result === 'object' && result !== null; const isFunction = typeof result === 'function';

      return (isObject || isFunction) ? result : obj; }

    • Object.create 做了些什么

      • 创建临时对象

      • 绑定原型

    • JS 如何实现类

      • function Dog (name){ this.name = name } Dog.prototype.say = function(){ console.log(this.name) } const d1 = new Dog("xxx") d1.say() // xxx

        • 缺点 ts 中不好写
      • class Dog { constructor(name){ this name. = name } say(){ console.log(this.name) } } const d1 = new Dog("xxx") d1.say() // xxx

    • js 如何实现继承

      • 通过构造函数实现继承

        • function Animal (legs){ this.legs = legs } Animal.prototype.kind = "动物"

    function Dog(name){ this.name = name Animal.cal(this,4) // 继承 属性 }

    // 继承 原型 var f = function(){} f.prototype = Animal.prototype Dog.prototype = new f()

    Dog.prototype.say = function(){ console.log(this.name) }

    Code
    - class 实现继承 - class Aminal {

    construct(legs){ this.legs = legs } }

    class Dog extends Animal { construct(name){ super(4) this.name = name } say(){ console.log(this) } }

    • 函数

      • js 中规定, 声明的函数中,它的prototype.constructor = 函数本身
    • JS 的立即执行函数是什么

      • 声明一个匿名函数并立即执行它

      • 作用:创建一个局部作用域

      • 优点:兼容性好

      • 缺点: 写法丑

    • JS 闭包是什么 有什么作用

      • 闭包是js 一种语法特性 闭包 = 函数 + 自由变量

      • 作用

        • 避免污染全局环境

        • 提供局部变量的局部访问器

        • 维持变量不被垃圾回收

      • 优点: 好用

      • 缺点: 使用不好导致内存泄漏

    • 箭头函数

      • 箭头函数没有自己的this,它的this是继承自外层作用域的this, 避免this 绑定错误的问题

      • 没有arguments对象

      • 不是作为构造函数使用,不能使用new 操作父

      • 好处

        • 代码更简洁,语法更清晰,可以减少代码量。

        • 不需要使用bind方法来绑定this,避免了this绑定错误的问题。

    • 作用域类型

      • 全局作用域

      • 模块作用域

      • 函数作用域

      • 块级作用域(ES6)

    • async 和 defer 区别

      • async : 啥时候加载完啥时候执行

      • defer : 脚本总是在 DOM 准备好时执行(但在DOMContentLoaded事件之前)

    • Instanceof 原理

      • 通过 找左侧实例的原型,直到null left.proto === right.prototype

      • 手写

        • function myInstanceof(left, right) { let rightPrototype = right.prototype; // 获取构造函数的显式原型 let leftProto = left.proto; // 获取实例对象的隐式原型 while (true) { // 说明到原型链顶端,还未找到,返回 false if (leftProto === null) { return false; } // 隐式原型与显式原型相等 if (leftProto === rightPrototype) { return true; } // 获取隐式原型的隐式原型,重新赋值给 leftProto leftProto = leftProto.proto } }
    • == 隐试转换的原理

      • 字符串和数字

        • 将字符串转为数字
      • 布尔和其他类型

        • 会将布尔转为数字
      • 对象和原始类型

        • 会将对象转为原始类型

          • 不是基本类型,先调用valueOf,还不是基本类型,调用toString,,还不是就报错 判断完成
    • 浏览器内核

      • GUI 渲染线程

        • 主要负责页面的渲染,解析HTML CSS 构建DOM 树, 布局和绘制

        • 当页面需要重绘绘者由于某种 操作引发的回流时将执行该线程

        • 该线程和js 线程互斥,当执行js引擎线程时,GUI 线程被刮起。当任务队列空闲时,主线才会执行GUI 渲染

      • JavaScript 引擎线程

        • 负责处理JS 脚本,执行代码

        • 负责执行准备待执行的事件,比如,定时器结束,异步操作成功后,将一次进入任务队列,等待js 引擎线程的执行

        • 当js 引擎线程执行了js 脚本时间过长时,页面渲染将会阻塞。

      • 定时器触发线程

        • 执行异步定时器一类的函数的线程,如: settimeout, setinterval

        • 主线程依次执行代码时,遇到定时器,会将定定时器交给该线程处理,当计数完成时,事件触发线程会计数完毕的事件,加入到任务队列的尾部,等待JS 引擎线程的执行

      • 事件触发线程

      • 异步http 请求线程

    • 尺寸和位置

      • DOM 对象 相关尺寸和位置的属性

        • 只读属性

          • clientWidth / clientHeight

            • content+padding 指元素可视区域的宽度和高度
          • offsetWidth / offsetHeight

            • content+padding + border
          • clientTop / clientLeft

            • border 的宽高
          • offsetTop / offsetLeft

            • 当前元素距离外层最近定位元素的位置,通过offetParent 可以获取离自己最近的定位元素
          • scrollHight / scrollWidth

            • 滚动区域的宽高
        • 可读可写的

          • scrollTop / scrollLeft

            • 元素被卷起的高度和宽度
          • dom.style.xx

            • 修改行内样式
      • Event 事件对象的尺寸和位置属性

        • clientX / clientY

          • 鼠标点击的位置,相对于浏览器窗口的可视区域
        • screenX / screenY

          • 鼠标位置相对于整个屏幕的位置
        • offsetX / offsetY

          • 鼠标点击点,相对于鼠标所在的元素的左上角为原点
        • pageX / pageY

          • 细分主题 1
    • 手写

      • 手写节流 throttle

        • 技能冷却中,被触发后n秒后不能再次出发

        • const throttle = (fn,time)=>{ let flag = false let timer = null return (...args)=>{ if(flag) return fn.call(undefined, ...args) flag = true timer = setTimeout(() => { flag = false clearTimeout(timer) }, time); } }

      • 手写 防抖 debounce

        • 回城被打断, 触发后倒计时 n秒后执行,如果在倒计时没结束时被再次触发,则重新倒计时n秒

        • const debounce = (fn, time) => { let timer = null return (...args) => { clearTimeout(timer) timer = setTimeout(() => { fn.call(undefined, ...args) }, time); } }

      • 发布订阅模式

        • class eventHub { queueMap = new Map()

    on(name, fn) { const q = this.queueMap.get(name) || [] q.push(fn) this.queueMap.set(name, q) } emit(name, data) { const queue = this.queueMap.get(name) || [] queue.map(fn => { fn(data) }) } off(name, fn) { const queue = this.queueMap.get(name) || [] const index = queue.indexOf(fn) if (index < 0) return queue.splice(index, 1) this.queueMap.set(name, queue) } }

    Code
    - 手写 AJAX - const ajax = (method, url, data, success, fail) => {

    const request = new XMLHttpRequest() request.open(method, url) request.onreadystatechange = function () { if (request.readyState === 4) { if (request.status >= 200 && request.status < 300 || request.status == 304) { success(request) } else { fail(request) } } } request.send(data) }

    Code
    - 手写promise - function runMicroTask(fn) {

    // eslint-disable-next-line no-undef if (process?.nextTick) { // node // eslint-disable-next-line no-undef process?.nextTick(fn); } else if (window.queueMicrotask) { // 浏览器 window.queueMicrotask(fn); } else { // 其他清情况 setTimeout(fn, 0); } }

    function isPromise(obj) { return !!(obj && typeof obj === "object" && typeof obj.then === "function"); }

    class MyPromise { _state = "padding"; _result = undefined; _handlers = [];

    constructor(executor) { try { executor(this.#resolve.bind(this), this.#reject.bind(this)); } catch (error) { this.#reject(error); // } }

    // 修改状态 changeState(state, result) { if (this._state === "padding") { this._state = state; this._result = result; } this._runHandler(); }

    _runHandler() { if (this._state === "padding") { return; }

    Code
    while (this._handlers.length) { const handler = this._handlers.shift(); this._runOneHandler(handler); }

    }

    _runOneHandler({ onFulFilled, onRejected, resolve, reject }) { runMicroTask(() => { const nextExecutor = this._state === "fulfilled" ? onFulFilled : onRejected; const nextResolve = resolve; const nextReject = reject;

    Code
    if (!(nextExecutor instanceof Function)) { this._state === "fulfilled" ? nextResolve(this._result) : nextReject(this.result); } try { const nextResult = nextExecutor(this._result); if (isPromise(nextResult)) { nextResult.then().then((res) => { nextResolve(res); }, nextReject); } else { nextResolve(nextResult); } } catch (error) { nextReject(error); } });

    }

    #resolve(data) { this.changeState("fulfilled", data); }

    #reject(reason) { this.changeState("rejected", reason); }

    then(onFulFilled, onRejected) { return new MyPromise((resolve, reject) => { this._handlers.push({ onFulFilled, onRejected, resolve, reject }); this._runHandler(); }); }

    catch(onRejected) { return this.then(null, onRejected); } finally(fn) { return this.then( (data) => { fn(); return data; }, (reason) => { fn(); throw reason; } ); }

    static resolve(data) { // if (data instanceof MyPromise) { // return data; // } return new MyPromise((resolve, reject) => { if (isPromise(data)) { data.then(resolve, reject); } else { resolve(data); } }); }

    static reject(reason) { return new MyPromise((resolve, reject) => { reject(reason); }); } static all(promises) { return new MyPromise((resolve, reject) => { const result = []; let count = 0; let fulfilledCount = 0; for (const p of promises) { let index = count++;

    Code
    if (isPromise(p)) { p.then( (data) => { fulfilledCount++; result[index] = data; if (fulfilledCount === count) { resolve(result); } }, (reason) => { reject(reason); } ); } else { fulfilledCount++; result[index] = p; } } if (count === 0) { resolve(result); } });

    } // 返回所有promise 的状态和结果 static allSettled(promises) { const result = []; for (const p of promises) { if (isPromise(p)) { result.push( p.then( (value) => ({ status: "fulfilled", value }), (reason) => ({ status: "rejected", reason }) ) ); } else { result.push({ status: "fulfilled", value: p, }); } } return MyPromise.all(result); } static race(promises) { return new Promise((resolve, reject) => { for (const p of promises) { MyPromise.resolve(p).then(resolve, reject); } }); } }

    Code
    - 手写 promise.all - 功能,输入list,当全部成功 时 resolve,遇到一个reject 时reject - myAll = function (list) {

    const result = [] let count = list.length return new Promise((resolve, reject) => { list.forEach((promise, index) => { promise.then(res => { result[index] = res --count if (count === 0) { resolve(result) } }, err => { reject(err) }) });

    }) }

    Code
    - 手写深拷贝 - 方法一 - JSON.parse(JSON.stringify(obj)) - 缺点:

    不支持 Date 正则 undefind 函数等数据

    Code
    - 方法二 - 递归法

    判断类型,检查环!

    Code
    - function deepClone(obj) {

    const cache = new WeakMap() const clone = (a) => {

    Code
    // 检车引用环 if (cache.has(a)) { return cache.get(a) } // 判断对象 if (a instanceof Object) { let result = undefined if (a instanceof Function) { // 函数 if (a.prototype) { result = function () { return a.apply(this, arguments) } } else { // 箭头函数 result = (...args) => { return a.apply(undefined, args) } } } else if (a instanceof Array) {// 数组 result = [] } else if (a instanceof Date) { // 日期 result = new Date(a.valueOf()) } else if (a instanceof RegExp) { // 正则 result = new RegExp(a.source, a.flags) } else { result = {} } cache.set(a, result) // 拷贝函数的属性 for (const key in a) { result[key] = clone(a[key]) } return result } else { // 基础数据类型 return a }

    } const newobj = clone(obj)

    return newobj

    }

    Code
    - 数组去重 - 1. 使用 使用 Set 和展开操作符 - [...new Set([1,1,2,2,1,1])] - 2. Array.from + Set - Array.from(new Set([1,1,2,2,1,1])) - 3. 遍历法 使用map 计数 - function uniq(list) {

    const map = new Map() const result = [] list.forEach(item => { if (map.has(item)) { map.set(item,item) result.push(item) } }); return result }

    Code
    - 手写事件委托 - document.querySelector("ul").addEventListener("click", function (e) {

    if (e.target.tagName.toLowerCase() === "li") { // 执行逻辑 } }) // bug 在于如果点击了li里面的span ,就没有触发fn

    Code
    - // 升级

    function delegate(element, eventType, selector, fn) { element.addEventListener(eventType, e => { let el = e.target while (!el.matches(selector)) { if (element == el) { el = null break; } el = el.parentNode } el && fn.call(el, e, el) }) return element }

    Code
    - 好处: 节省监听器,动态监听 - 坏处: 不容易排插当前元素有哪些监听事件 - target 和 currentTarget 的区别 - currentTarget 表示的监听的元素

    target 表示触发的元素(点击了那个元素,target 就是那个)

    Code
    - 手写拖拽 DIV - 要点 - 注意监听范围,不能只监听div - 不要使用drag 事件, 很难用 - 使用 transform, 比较高效,top 可能会造成卡顿 reflow - 记录鼠标开始位置,移动时算出,移动的距离,

    元素的初始位置+移动的距离

    Code
    - let drag = false;

    let position = [0, 0]; let translate = [0, 0];

    const box = document.querySelector(".box"); box.addEventListener("mousedown", function (event) { drag = true; position = [event.clientX, event.clientY]; });

    box.addEventListener("mouseup", function () { drag = false; });

    document.addEventListener("mousemove", function (event) { if (drag) { const moveX = translate[0] + event.x - position[0]; const moveY = translate[1] + event.y - position[1]; box.style.transform = translate(${moveX}px,${moveY}px); translate = [moveX, moveY]; position = [event.x, event.y]; } });

    纯函数

    • 一个函数的返回结果只依赖于它的参数

    • 函数在执行过程里面没有任何副作用【不会造成外部变量的改变】

    HTTP

    GET 和 POST 有什么区别

      1. 幂等性 GET 是读, POST是写。
      • 用浏览器打开网页发送的是GET请求,想要POST打开网页需要使用form 标签,
    Code
    - GET 打开网页是无副作用的。POST 打开页面刷新时需要确认 - GET的结果会被缓存起来, POST 的结果不会被缓存
    • 2. 请求参数

      • 一般,GET 请求参数放在 URL 里,POST 请求数据放在body 消息体里

      • GET 比 POST 更不安全,参数直接暴露在URL 上,敏感信息容易泄漏

      • GET 请求的URL 长度会有限制,而POST 放在body 体里没有长度限制

        • 长度限制的原因: 浏览器和服务器的限制,和HTTP协议本身没有关系。浏览器和服务的不同。对大小的限制也有所有不同的。
      • TCP packet

        • GET 产生一个TCP 数据包,而POST 产生两个或以上 TCP 数据包

    HTTP 缓存策略

    • HTTP 1.1

      • 正常缓存:查看是否过期 Cache-control: max-age=3600 Etag: ABC

        • 协商内容: If-None-Match: ABC 响应状态码: 304 或者200
    • HTTP 1.0.

      • 正常缓存:查看是否过期 Expires: Wed,21 Oct 2015 0:30:00 GMT Last-Modified: Wed,21 Oct 2015 0:30:00 GMT

        • 内容协商: If-Modified-Since: Wed,21 Oct 2015 0:30:00 GMT 响应状态码: 304 或者200
      • 缺点:

    1. 用户本地时间修改导致文件过期
    2. 如果文件一秒钟被修改了N次,使用时间点无法判断
    • Pragma

      • MDN 已经明确不推荐使用,语意不明确,是对1.0 的补充

    http1 和 http2 的区别

    • HTTP2 使用二进制传输,而且head和body 分成帧来传输的,http1.1 是字符串传输

    • http2 支持多路复用, http1.1 不支持。多路复用简单来说就是一个TCP连接从单车道,变成几百个双向通行的车道

    • http2 可以压缩header

    • http2 支持服务器推送,但是http1.1 不支持。(实际没有多少人使用)

    HTTP 和HTTPS 的区别

    • HTTPS = HTTP + SSL/TLS (安全层)

    • 区别

      • http 是明文传输的,不安全。 https 是加密传输

      • http 使用的是80, https 使用的443

      • http 快,https 慢

      • https 是需要使用证书的

    三次握手,四次挥手

    • 创建TCP 连接 三次握手

      • 浏览器向服务器发送TCP数据 SYN(X)

      • 服务端向浏览器发送TCP 数据 ACK(X+1) SYN(Y)

      • 浏览器向服务器发送TCP 数据ACK(Y+1)

    • 断开链接 四次挥手

      • 浏览器向服务器发送TCP数据 FIN(x)

      • 服务器向浏览器发送TCP数据ACK(x+1)

      • 服务器向浏览器发送TCP 数据FIN(y)

      • 浏览器向服务器发送TCP数据ACK(y+1)

    同源策略和跨域

    • 同源策略 是 协议 域名 端口完全一致表示同源 (在浏览器环境)

      • 优点: 保护用户隐私和数据安全

      • 缺点: 前端需要访问另外后端域名的借口是,会被浏览器阻止其获取到的响应

        • A 网站中访问B 网站的接口,请求会发出来,但是响应会被浏览器屏蔽
    • 解决跨域方案

      • JSONP

        • 细分主题 1
      • CORS

        • 在请求头添加 Access-Control-All-Origin: 站点
      • 服务器代理

    浏览器存储对比

    • cookie, localstorage

      • cookie 会被发送到服务器,而 localstorage不会

      • cookie 大小为4k 左右 localstorage 大小为 5MB 左右,各个浏览器不同

    • localstorage, sessionstorage

      • local storage 一般不会过期,除非用户手动清除

      • sessionStorage 在会话结束时过期(如关闭浏览器后,具体由浏览器自行决定)

    • cookie session

      • cookie 存在浏览器的文件里,而 session 存在服务器的文件中

      • session 是基于cookie 实现的,sessionID 存储在cookie 中

    TypeScript

    TS 和JS 的区别是什么

      1. TS = JS + Type(ts 是js的超集)
    • 执行环境层面: js 可以在浏览器服务器直接执行,但是不能执行TS(Deno 可以执行TS)

    • 编译层面: TS 有编译阶段

    • 编写层面: TS 更难写,但是类型安全

    • 文档层面: TS 的代码写出来就是文档,IDE 也有提示。js 主要靠TS

    any, unknown,never 的区别

    • any 和 unknow 都是顶级类型,但是unknown 比any 的类型检查更严格

    • any 什么检查都不做。unknown 要收窄类型

    • never 是底类型,表示不应该出现的类型

    type 和 interface 的区别是什么?

    • 组合方式:

      • interface 使用 extends 实现继承, type 使用& 来实现联合类型
    • 扩展方式:

      • interface 可以重复声明, type 一个类型只能声明一次
    • 范围不同:

      • type 适用于基本类型, interface 一般不行
    • 命名方式:

      • interface 会创建新的类型名, type 只创建类型别名,并没有创建类型

    TS 工具类型的作用和实现?

    • Partial

      • 部分类型(在每个属性前加?)

      • type Partial = { [P in keyof T]?: T[P]; };

    • Required

      • 必填类型(删除每个属性的?)

      • type Required = { [P in keyof T]-?: T[P]; };

    • Readonly

      • 只读类型(每个属性前加 readonly)

      • type Readonly = { readonly [P in keyof T]: T[P]; };

    • Exclude

      • 排除类型

      • type Exclude<T, U> = T extends U ? never : T;

    • Extract

      • 提取类型

      • type Extract<T, U> = T extends U ? T : never;

    • Pick

      • 从类型T 中 选取指定的属性

      • type Pick <T, K extends keyof T> = { [P in K]: T[P] }

    • Omit

      • 排除Key类型

      • type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

    • RenturnType

      • 返回值类型

      • type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

    • Record

      • 创建对象

      • type Record<K extends keyof any, T> = { [P in K]: T }

    VUE

    vue2

    • 生命周期

      • beforeCreate

      • created

      • beforeMount

      • mounted

        • 数据请求放在这里合适
      • beforeUpdate

      • updated

      • activated

        • 被keep-alive 缓存的组件被激活时调用
      • deactivated

        • keep-alive 组件 失活时调用
      • beforeDestroy

      • destroyed

      • errorCaptured

        • 组件报错,或者子组件报错时调用
    • 数据请求放在那个周期

      • 放在Mounted 中 放在created: 在SSR 中会被执行两次
    • 组件间通信

      • 父传子

        • props 和事件
      • 爷孙组件

        • 使用两次 父传子

        • 使用【provide + inject】

      • 任意组件

        • 自建 eventBus

          • 缺点 事件多了以后很混乱,难易维护
        • Vuex

    • Vuex 使用过吗? 怎么理解?

      • vuex 是专门为vuejs 应用程序开发的状态管理模式库

      • 核心概念和作用

        • store

          • 是个大容器,包含一下所有内容
        • state

          • 用来读取状态, 辅助函数: mapState
        • Getter

          • 用来读取派生状态, 辅助函数:mapGetters
        • Mutation

          • 用于同步提交状态变更 辅助函数: mapMutations

            • 在VUE 代码中,通过$commit("函数名") 提交
        • Action

          • 异步变更状态,但是他提交的是mutation,而不是直接变更状态,通过commit 触发 mutation函数

            • 在VUE代码中通过$dispatch("函数名") 触发
        • Module

          • 用来给store 划分模块,方便维护
      • Mutation 和action 为什么分开

        • 为了方便维护代码(可是 Pinia 就把 Mutation 和 Action 合并了)

        • 更好的管理状态变更的逻辑和异步操作

    • Vue Router

      • 是 vue 的官方路由,他与vue核心深度集成,让用vue构建单页面应用变得轻而易举

      • 核心概念

        • router-link

          • 点击跳转
        • router-view

          • 容纳视图
        • 路由模式,Hash 、History

          • 一个使用的hash一个使用的History api

          • history 需要后端或者nginx 配合,一个不需要

        • 导航守卫

          • router.beforeEach((to,from,next)=>{ if(没登陆&& to.path!=="login"){ return next('/login') } next() })
        • 懒加载

          • import()
    • 如何实现的双向绑定

      • 写法

        • 使用 v-model / .sync, v-model = v-bind:value+ v-on:input
      • 实现

        • 通过 Object.defineProperty API 给data 创建 getter 和 setter, 用于监听data 的改变,data 一变化就会通知 UI 变

    vue3

    • 为什么使用 Proxy

      • 弥补Object.defineproperty 的两个不足

          1. 动态创建data属性需要使用到Vue.set来赋值
          1. 基于性能考虑,Vue2篡改了数组的7个api
      • defineproperty 需要提前递归遍历data做到响应式,而proxy 可以做到深层数据的响应时(惰性)

    • 为什么使用Composition API

      • composition api 比 mixins 高阶组件,extends,Rendless components 更好

        • 模版中的数据来源不清晰

        • 命名空间冲突

        • 性能

      • 更适合TS

    • 有哪些升级

      • 使用 createAPP 代替 new Vue

      • 跟组件可以不值一个根组件

      • 新增传送门 teleport

      • destroyed 被改名为unmounted

      • ref 属性支持函数

    • Vue2 和Vue3的变化

      • 源码上的变化

          1. Monorepo
          • vue2 的源码托管在src下面的根据功能才分

            • compiler:编译器

            • core: 和平台无关的运行时代码

            • platforms: 平台

          • vue3 monorepo 形式,

            • 每个包都有各自的api,和测试
        • typescript

          • vue2 使用的Flow.js,是facebook 推出的类型系统

          • vue3 TypeScript

      • 性能变化

        • 源码体积缩小

          • 移除冷门功能。 filter inline-template

          • 生产环境使用Rollup 进行构建, 利用Tree-Shaking 减少用户打包体积

        • 数据劫持优化

          • Object.definePropety

          • Proxy

        • 编译优化

          • 静态提升

          • 预字符串化

          • 缓存事件处理函数

          • Block Tree

          • PatchFlag

        • Diff 算法优化

          • 双端DIFF算法+最长递增子序列

          • 快速diff 算法

      • 语法Api 的变化

        • Vue2 Options api

        • Vue3 Options Api + CompositionApi (逻辑更加集中)

          • 查看功能实现集中

          • 代码风格可复用的颗粒度更细

        • 其他变化

          • vue2 使用通过实例化方式创建应用

            • 缺点: 当页面上有多个vue 应用时,配置会有影响

              • Vue.use() vue.mixin() Vue.component() new Vue({}).$mount("#app1") new Vue({}).$mount("#app2")
          • Vue3 使用createApp 的方式创建

            • createApp(RootApp).use().mixin().component().mount("#app1")
      • 引入RFC

    • 核心模块

      • 编译器

      • 渲染器

        • 根据拿到的Vnode 进行节点挂载和更新

        • 细分主题 2

      • 响应式系统

    为什么Vue3取消掉Vue的构造函数

    • VUE中的全局构造函数带来了很多问题

        1. 调用构造函数的静态方法会对所有的VUE应用生效,不利于隔离不同的应用
        1. Vue2 的构造函数集成了太多的功能,不利于Tree Shaking ,Vue3 把这些功能使用普通的函数到处,能够充分利用treeshaking 优化打包体积
        1. Vue2 没有把最组件实例和应用实例两个概念区分开来,在vue2中通过new Vue创建对象, 即使一个Vue 应用,同时又是一个特殊的Vue 组件, Vue3 把这个概念区分开,createApp 创建Vue 应用。

    Vue3 响应式相较于vue2 又什么改变

    • vue2 使用Object.definePropety vue3 使用 Object.definePropoty+ Proxy

    • 响应式数据的变化

      • vue2: 通过data创建响应式数据 vue3: 通过ref reactive 等方式创建

        • ref使用Object.definePropoty+ Proxy

        • reactive 使用 Proxy

    • 依赖收集上的区别

      • vue2: Watcher+ Dep 每一个响应式数据都有一个Dep 实例,内部有一个数组,用户存醋依赖这个属性的watcher 当属性值变化时,dep 就会通知所有的Watcher去做更新操作

      • Vue3 : weakmap+map+set Weakmap键对应的是响应式对象,值是一个map,map 的键是该对象的属性,值是一个set,Set 里面存储了所有依赖该属性的effect 函数

    Vue 的nextTick 是如何实现的

    • 本质是将回调函数包装成微任务放在微任务队列中,当浏览器执行完渲染之后,会优先执行微任务

      • VUE2 : 为了兼容浏览器版本,会根据不同情况包装微任务

        • 优先使用Promise

        • 不支持Promise ,使用MutationObserver

        • IE 环境, 使用setiimmediate

    diff 算法

    • 头头对比

    • 尾尾对比

    • 旧头新尾

    • 旧尾新头

    React

    虚拟DOM 的原理

    • 虚拟DOM 是对真实DOM 的一种抽象虚拟节点,使用js 的对象来模拟DOM 节点 然后将其渲染成真实dom

      • const vnode = { tag: "div", props: { id: "xx" }, children: [ { tag: "span", props: { className: "xxx" }, children: ["hi"] } ] }
    • 将虚拟节点渲染成真实节点

      • 调用render 方法

        • 判断如果是字符串或者数字,创建一个文本节点

        • 根据标签tag 创建 Element

        • 给标签添加属性

        • 遍历子节点并获取真实dom,插入到当前节点

        • 虚拟DOM 中缓存真实 DOM 节点

        • 返回DOM 节点

        • 如果节点发生变化,并不会直接把新的虚拟及诶单渲染到真实节点上,而是先通过diff 算法 得到一个patch 补丁,再更新到真实节点上

      • 解决了什么问题

        • DOM 操作的性能问题,通过DOM的虚拟和Diff 算法减少不必要的DOM 操作,保证性能不太差

        • DOM操作不方便问题,以前需要记各种api,现在只需要关注数据set State()

      • 优点:

        • 为React提供了跨平台能力,可以使用虚拟dom 渲染成其他东西

        • 细分主题 2

      • 缺点

        • 性能要求极高,还是得用真实DOM 操作

        • react 为虚拟DOM 创造了合成事件, 跟原生DOM事件不太一样,工作中需要注意

          • react 事件都是绑定到跟元素的,自动实现了事件委托

          • 混用合成事件 和 原声DOM 事件 可能会有 bug

    DOM 的 Diff 算法是怎么样子的

    • 对比两个新旧DOM 之后得到一个patch, react 在用patch 更新真实dom

      • 先对比根节点

        • 如果跟节点类型变化,直接任务整棵树变化,直接删除就DOM 创建新的真实DOM

        • 如果类型没有变化,看属性变化 变化了就只更新节点属性,

    • 双端较差对比的算法

    React 生命周期函数

    https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

    • constructor

    • getDerivedStateFromProps

    • sholdComponentUpdate

    • render

    • componentDidMount

    Code
    - 请求一般放在这里
    • getSnapshotBeforeUpdate

    • componentDidupdate

    • componentWillUnmount

    React 组件通信

    • 父子组件 props + 函数

    • Context.Provider. 和 Context.Consumer

    • Redux

    • Mobx

    • Recoli

      • react 官方的局部状态管理

    如何理解Redux

    • 是一个状态管理库

    • 核心

      • State

        • 状态数据
      • Action

        • type + payload(荷载)
      • Reducer

        • 细分主题 1
      • Dispatch

        • Dispatch 触发action
      • Middleware

        • 细分主题 1
    • ReactRedux 核心

      • connnect()(Component)

      • mapStateToProps

      • mapDispatchToProps

    • 常见的中间件

      • Reduc-thunk

      • Redux-promise

    什么是高阶组件 HOC

    • 参数是组件,返回的函数也是组件

    • 举例

      • React.forwardRef

        • 组定义组件没有ref 可以通过forwordRef React.forwardRef((props,ref)=>{ return() })
      • ReactRedux.connect

      • ReactRouter.withRouter

    hooks(16.8) 相对于class 的优势

    • 简化组件逻辑

      • 可以将逻辑相关的逻辑组织到一起,而不是分散到不同的生命周期方法中
    • 减少样板代码

      • 组件函数可以直接使用状态和其他react 特性,而不需要创造类、编写构造函数、使用this,等减少编写和阅读样板代码的量,使代码简洁
    • 更好的复用性和组合型,

      • 可以将复用的逻辑自定义hook, 并在多个组件共享使用,提高代码的复用性和组合型
    • 避免类组件的限制

      • 无法在不使用HOC 的情况下共享逻辑,难以重用非UI相关的逻辑
    • 性能优化更好

      • 由于hooks 的设计。可以更高的更新和优化组件的更新和渲染。我门可以更细颗粒度的控制副作用的执行机制,从而减少不必要的更新和渲染。提高性能

    MessageChannel 是什么

    • message channel 可以产生一个宏任务,React Schduler 中,没有执行完的任务,放在下一次执行

    • 为什么不使用settimeout(fn,0)

      • 当潜逃层级超过5层时,有默认4ms 的延迟
    • 为什么不使用requestAnimationFrame

      • 这个只能在重新渲染之前,执行一次,如果包装成一个任务放在任务对立中,如果没有到重新渲染时间时,会一直不会执行,并且有兼容问题 Safari 和Edge 浏览器将rAF 放在渲染之后执行 Chrome 和FireFox 放在渲染之前 标准: 放在渲染之前

    Fiber架构

    • Fiber 和之前的Stack比较

      • 虚拟DOM 比较时

        • Stack: 采用递归,计算时比较耗时

        • Fiber: 采用链表, 实现时间切片,防止js 计算占用太多时间,从而导致浏览器丢帧的方案

    • 对Fiber 的理解

      • Fiber 是一种架构,成为Fiber 架构

        • V16之前使用stack reconciler, 之后成为Fiber 架构 Fiber reconciler, 各个fibernode 通过链表链接起来
      • FIber 是一种数据类型

        • 本质是一个在内存中的对象,之前使用的react 元素,升级之后每个fibernode 对象包含react 元素类型,周围链接的fibernode 以及dom 的相关信息

          • function FiberNode(tag,pendingProps,key,mode){ this. tag = tag this.key = key this.elementType = null this.type = null this.stateNode = null// 真实dom }
      • Fiber 是动态的工作单元

        • 在每个FiberNode 中保存了本次更新中React 元素变化的数据,要执行的工作(增,删,改),以及副作用的信息
    • 为什么指向父FiberNode 时,为什么叫return,

      • return 表示FiberNode 执行完complateWork 后返回的下一个节点,这个有个return 的动作,因此通过return 来指代父FiberNode
    • Fiber 双缓冲是什么

      • 它的显卡的工作原理类似

        • 显卡分为前后缓冲区

          • 前缓冲区

            • 他会在屏幕中渲染图像
          • 后缓冲区

            • 新合成图像会被写入后缓冲区
          • 后缓冲区写入完成后,前后缓冲区调换,将数据保存在缓冲区互换成为双缓冲区的

      • Fiber 架构也使用到了这个技术,Fiber 架构中,存在两个FiberTree, Wip Fiber Tree 在内存中完成更新 Current Fiber Tree 是最终要渲染的树,两个树通过alternate指针互相指向,再一次渲染中直接服用wip Fiber tree 作为下一次的渲染树,而上一次的渲染树有作为新的wip Fiber Tree. 加快dom 节点的更新与替换

        • React中需要接手两个FiberTree,两个FiberNode 通过 alternate 互相指向
    • 位运算

      • Fiber 的flags

        • 这些状态都是用来表示Fiber 的状态的,之所以抽离Fiber 的状态,因为这种操作很高效,,针对Fiber 的操作,可能有增加、删除】修改,不直接操作,而是打上一个flag,后面流程会针对Flag 的fiber 统一操作
      • Lane 模型

        • 也是一套优先级机制
      • 上下文:

    React 渲染流程

    • 阶段

      • 调度器(Scheduer)

        • 调度任务,为任务排序优先级,让优先级高的任务进入Reconciler

        • React 16之前使用的Stack 架构,所有任务同步执行,可能会导致浏览器丢帧,卡顿。 16之后引入, Fiber, Scheduer

        • 实现原理: 使用浏览器API requestIdleCallback(由于有兼容性问题,React 自己实现了一个自己的requestIdleCallback)

          • 插入一个函数,该函数可以在浏览器空闲时期被调用,使开发者能够在主事件循环执行后
      • 协调器 (Reconciler)

        • 生成Fiber 对象,收集副作用,找出哪些节点发生了变化,打上不同的flags, 著名的diff 算法也是在这个组件中执行的

        • 类组件和函数组件会在这个阶段被调用

      • 渲染器(Renderer)

        • 根据协调器,计算出来的虚拟DOM 同步渲染节点到视图上。
    • 分为两大阶段

      • render 阶段

        • 主要有调度器和协调器 处理,此过程在内存中运行,异步可中断

          • 调度器(Scheduer)

            • 主要工作是调度任务,让所有任务有优先级的概念, 紧急的任务可以优先执行(例如用户输入), scheduler 实际上在浏览器中有原声的API 实现 requestIdleCallback, 由于兼容问题,React 放弃使用并自己实现了这套机制, 并发布了单独的包,这意味着不仅React 可以使用,其他项目涉及到任务调度也可以使用Scheduler
          • 协调器(Reconciler)

            • 协调器是render 的第二届阶段, 采用深度优先遍历并创建一个个FiberNode,将其串联起来。遍历时分为递和归, 递阶段会执行beginWork,该方法传入的FiberNode 创建下一级 FiberNode, 而归阶段,会执行CompleteWork 方法,做一些副作用的收集

              • BeginWork(递)

                • 根据传入的FiblerNode 创建下一级的FiberNode

                  • 判断当前流程是mount/updata (依据: currentFIberNode 是否存在)

                    • mount

                      • mountChildFiber
                    • updata

                      • 判断Wip filber node 是否可以复用

                        • 不能复用

                          • 根据wip.Tag 进行不同分支的处理 (React中定义了28种tag)

                            • 根据Reconcile 算法生成 下一级FiberNode(Diff 算法)
                        • 可以复用

                          • 路径优化
              • ComplateWork(归)

                • 根据wip.tag 区别对待

                  • mount

                    • CreateInstance: 创建DOM 元素

                      • AppendAllChildren: 对childen 执行Appendchild

                        • finalizeinitialChildren 设置DOM 属性
                  • update

                    • updateHostComponent 判断是否有属性变化,有则查找变化的属性,并标记updateFlag 主要逻辑在diffProperties(该方法遍历两次) 第一次:标记更新前有更新后没有的属性(删除的属性) 第二次:主要标记,更新前后变化的属性,(更新的属性)

                      • 所有更新的属性key 和value,都会保存在,当前FIberNode.updateQueueQueue,属性都是以key-value 作为数组相邻两项的形式保存 [key1,value1,key2,value2,....]
                  • bubbleProperties: flag冒泡

                    • 主要作用:收集副作用,从下而上,很适合收集FiberNode中的 副作用

                    • 好处:通过任意一级的FiberNode.subtreeFlags 都可以知道该FIberNode以及子树是否存在副作用,从而判断是否需要执行副作用相关的操作

                    • 早起React 没有使用subTreeFlag,而是使用,effect list(链表)来收集副作用, subTree Flags 可以确定 fibernode 的子树是否有副作用

            • React 中的Diff 算法

              • Diff 指的是,Current FiberTree he JSX 对象之前的对比,然后生成WipFiberTree currentFiberTree -> diff jsx 对象(React 元素) -> WIP FIberTree

              • 注意⚠️

                • 涉及到VDOM 的都会有diff ,只是叫法不一样

                • Diff 算法本身就会有性能的消耗,React中提到:即便采用最前沿的算法,完全对比两个树,时间复杂度O(n^3)

              • React 为了降低时间复杂度设置了三个限制

                • 只对同级别元素进行DIff

                  • 如果一个元素在前后两个更新中跨越层级,React 不会复用它
                • 两个不同类型的元素会产生不同的树,

                  • 元素从div 变成了p , React 会销毁div以及子孙元素,新建p元素以及子孙元素
                • 开发者可以通过Key ,来标记那些元素能够保持稳定

              • diff 分两种情况

                • 跟新后只有一个元素

                  • 此时会根据newChild 创建对应的wipFilberNode,对应的流程就是单节点diff (新Node:1子节点,旧Node: N 个子节点)

                    • 单节点Diff 是否可以复用

                        1. 判断KEY 相同
                        • Key 不同

                          • 若判断兄弟节点,是否有相同的,没有者不能复用,不会走步骤2
                        • Key 相同

                          • 判断Type
                        1. 判断Type
                        • 相同

                          • 复用
                        • 不同

                          • 无法复用,旧兄弟节点标记删除
                • 更新后有多个元素

                  • 此时会遍历newChild 创建出来的 wipFilberNode 以及他的兄弟元素, 此时对应的是多节点DIff

                    • 第一轮遍历

                        1. 判断 Key 和type 相同
                        • 复用
                        1. Key 相同, Type 不同
                        • 根据ReactElement 生成一个全新的fiber Node, 旧的FiberNode 放到deletions 数组中,统一删除 遍历不会结束
                        1. Key和Type 都不同 或 key 不同
                        • 终止遍历

                          • 意味着有新的React元素以及旧的Fiber Node没有遍历完,需要第二轮遍历
                    • 第二轮遍历

                        1. 只剩下旧的子节点:
                        • (删除)将旧子节点添加到deletions 中
                        1. 只剩下React元素
                        • (新增)根据Reactelement 元素来创建fiberNode 节点
                        1. 新旧节点都有剩余
                        • 将剩余的FiberNode 放到map中,遍历剩余新的React 元素

                          • (移动)从map中寻找可以复用的FiberNode 节点,如果可以找到拿来复用,
      • commit 阶段

        • 由渲染器执行,根据副作用进行UI 的更新,此过程不可中断,否则会导致UI 和数据显示不一致

          • 渲染器(Render)

            • 主要工作是将各种副作用(flags 表示)Commit 到宿主环境的UI中,整个阶段分为三个子阶段

              主要工作是将各种副作用(flags 表示)Commit  到宿主环境的UI中,整个阶段分为三个子阶段

              • BeforMutation阶段

                • commitBeforeMutationEffects

    commitBeforeMutationEffects_begin

    commitBeforeMutationEffects_complete

    Code
    - 逻辑主要集中在, commitBeforeMutationEffects_complete 中,这里执行了 commitBeforeMutationEffectsOnFIber - commitBeforeMutationEffectsOnFIber 逻辑 - class Component: 调用getSnapsshotBeforeUpdate - HostRoot: 清空HostRoot 挂载的内容,方便 Mutation 阶段渲染 - 主要内容 - commitXXXEffects - 每个阶段的入口函数,将firstChild复制给nextEffect

    执行 commitXXXeffects_begin

    Code
    - commitXXXeffects_begin - 向下遍历FiberNode - 满足条件是停止 - 1. 当前FiberNode 的子FiberNode 不包含该子阶段对应的flags - 2. 当前的FIberNode 不存在FiberNode - 3. 满足后进入 commitXXXEffects_complate - commitXXXeffects_complate - 1. 对FiberNode 执行flag 对应操作,也通知 - 2. 如果当前FiberNode 存在兄弟节点,对兄弟节点执行 commitXXXEffects_begin - 3. 如果不存在兄弟节点,对父节点执行commitXXXEffects_complate - Mutation阶段 - commitMutationEffects - 主要内容:插入移动Dom 元素 - commitMutationEffects_begin - 删除DM元素 拿到 deletions 遍历删除操作 - 删除操作调用了commitDeletion(逻辑比较复杂) - 子树中所有组件的unmount - 子树中所涉及到的ref属性的卸载操作 - 子树所有effect 相关的Hooks 的destory - commitMutationEffects_complete - commitMutationOnFiber - 执行移动更新 - 细分主题 1 - Layout阶段 - commitLayoutEffects

    commitLayoutEffects_begin

    commitLayoutEffects_complete

    Code
    - 主要逻辑在commitLayoutEffectsOnFiber - 对应class component: conponentDidMount/update - 对应FC 执行 useLayoutEffect 的或调 - 概括

    React 中的事件

    • React中有自己的一套事件系统,

    • 事件系统包含

      • 合成事件对象

        • SyntheticEvent(合成事件对象)

          • 是对浏览器原生对象的一层封装,兼容主流浏览器,同时拥有和原生事件相同的API
      • 模拟实现事件传播机制(捕获/冒泡)

        • 利用事件委托原理,基于FbierTree ,实现了事件捕获,事件冒泡的过程(类似原生事件的传递过程),并加入了很多特性

          • 不同事件对应不同的优先级

          • 制定事件名:统一采用onXXX 驼峰写法绑定事件

          • 定制事件行为:onChange 是和原生onChange 相同

    React 中的Hook

    • Hooks 中有三种策略(三种dispatcher)

      • HooksDispatcherOnMount

        • 负责初始化工作,让函数组件的一些初始信息挂载到FiberNode 上
      • HooksDispatcherOnUpdate

        • 函数组件更新的时候,会执行该方法,更新Fiber 中的信息
      • ContextOnlyDispatcher

        • 报错相关,防止开发者在函数组件外部调用Hook
    • Hooks 的数据结构

      • const hook ={ memoizedState: null// 保存的state 的值,而fiber 中保存的是Hook 的第一个链表 baseState: null// baseQueue:null, // queue: null, // next: null, // 下一个Hook的链表 }

      • 常见hook 的数据结构

        • useState: const [state,updateState] = useState(initialState)

          • { memoizedState: State 的值 }
        • useReducer: const [state,dispatch] = useReducer(reducer,{})

          • { memoizedState: State 的值 }
        • useRef: useRef(initialValue)

          • { memoizedState: {current: initialValue} }
        • useEffect: useEffect(callback,[...deps])

          • { memoizedState: callback,[...deps] 的值 }
        • useMemo: useMemo(callback,[...deps])

          • { memoizedState: callback,[...deps] 的值 }
        • useCallback: useCallback(callback,[...deps])

          • { memoizedState: [callback,[...deps] ] }
        • 有些不需要memoizedState 保存数据: useContext

      • 流程 执行 runderWithHook

        • mount

        • update

    • useRef

      • useRef 各个阶段

        • mount

          • 调用mountref
        • update

      • ref 工作流程

      • 保存对Dom 的引用

    • react 触发更新的方法

      • 方法,不同类型的update 数据结构是不同的

        • createRoot

        • setState

        • forceUpdate

        • useState dispather

        • userreducer dispatcher

      • Update 数据结构

        • 更新是有数据优先级 ,高优先级的update 能够中断低优先级

    Node

    什么是Event Loop(不停循环的处理这六个事件叫事件循环)

    • timer 阶段

      • 执行 settionout setInterval 的回调
    • I/O callbacks

      • 处理一些上一轮玄幻中的少数,未执行的I/O 的回调
    • idle prepare

      • 仅node 内部使用
    • poll 轮训阶段,停留时间最长,可以随时离开

      • 主要处理I/O事件 该阶段的Node 会不停询问操作系统有没有文件数据,网络数据等

      • 如果Node 发现有timer 快到时间了或者有setImmediate 任务,就会主动离开Poll jieduan

    • check 阶段

      • 主要处理 setImmedate 任务
    • close callback

      • 执行 socket 的close 事件 回调

    nextTick / window. queueMicrotask

    • process.nextTick(fn) 的fn 在什么时候执行

      • node 11 之前 在每个阶段的末尾集中执行(队尾执行)

      • Node 11 之后 会在每个阶段的任务间隙执行(插队执行)

        • 和浏览器保持一致了

    Promise.resolve(1).then(fn) 的fn 会在什么时候执行

    • 这里要看Promise 源码如何实现的,一般使用process.nextTick(FN )实现的,参考nextTick

    • 细分主题 2

    浏览器中任务和微任务是什么

    • 任务

      • script 标签, setTimeout.
    • 微任务

      • Promise.then, window.queueMicrotask MutationObserver(vue 的$nextTick 实现), Proxy

    网络

    五层网络模型

    • 应用层

      • HTTP FTP SMTP DNS POP3

        • 根据体的应用相关的i消息格式,邮件使用SMTP, 网页大都用HTTP
      • 应用层主要面向互联网中的应用场景,网页邮件文件中心等, 他的代表协议http, smtp...

    • 传输层

      • TCP UDP

        • TPC 保证消息的准确传输,

        • 如何保证消息的可靠传输 UDP 类似广播

      • 面向传输过程,比如TCP 协议保证可靠的传输,而UDP 协议是一种无链接的广播,他们启动不同的传输方式

    • 网络层

      • IP。路由器

        • 如何在互联网中找到对方(公网IP 找到的)
      • 主要解决如何定位目标一节如何寻找缀有路径的问题,IP

    • 数据链路层

      • mac 地址 交换机

        • 在子网中如何找到对方,
      • 它的作用是将数据在一个子网中有效传播,MAC 地址 交换机都属于该层

    • 物理层

      • 光纤

        • 如何传输二进制上面的信号
      • 主要解决二进制数据到信号之前的转换问题,集线器,双绞线,同轴电缆,光纤等。

    四层网络模型

    • 应用层

    • 传输层

    • 网络层

    • 物理链路层(数据链路层+物理层)

    七层网络模型

    • 应用层

    • 表示层

    • 会话层

    • 传输层

    • 网络层

    • 数据链路层

    • 物理层

    请求方法

    • 常见请求方法

      • GET

        • 表示向服务器获取资源,业务数据放在请求行中,无需请求体
      • POST

        • 表示向服务器提交资源,通常用于产生新数据,业务数据放在请求体中
      • PUT

        • 表示希望修改服务器的数据,业务代码放在请求体中
      • DELETE

        • 表示删除服务器数据,无需提交请求体
      • OPTIONS

        • 放生在跨域的预检请求中,表示客户端服务器申请跨域提交
      • TRACE

        • 用户测试和诊断
      • CONNECT

        • undefined

    GET 和POST 的区别

      1. 浏览器发送请求时不会附带 请求体
      1. GET 请求的传递信息有限,适合传递少量数据(浏览器对请求行 有大小的限制4K),Post 的数据放在请求体中,对大小没有限制 ,传输文件使用post
      1. GET 只能传递ASCII 数据,遇到非ASCII 数据需要进行编码, POST 没有限制
      1. GET 数据暴露在path中, 不安全
      1. 如果页面是通过post 方式打开的页面,刷新页面时会有提醒
      1. GET 请求可以被保存到浏览器书签中
    • 组成

      • Key: 表示唯一键值

      • value: 任意信息

      • doman: 表示cookie 属于哪个网站的

      • path: 该coolie属于这个网站的哪个路径的

      • secure: 是否使用安全传输

      • expire: 过期时间

    • 浏览器携带coookie流程

      • 检查cookie

        • 是否过期

        • 检查doman

        • path

        • secure(是否使用https 传输)

      • 浏览器把键值对拼接放在请求头中的cookie 字段中,上传给服务器

      • 细分主题 3

    • cookie 来源

      • 服务下发

        • 响应头中有Set-Cookie 字段(一次可以下发多一个cookie)

          • key=value;path=?;doman=?;expire=?;max-age=?;secure;httponly;
        • 如果不设置expare 也没有max-age 表示会话结束时过期(关闭浏览器标签时过期)

        • 细分主题 3

      • 浏览器端js 添加

    同源策略

    • 同源策略发生在浏览器环境中

      • 协议+主机+端口相同则表示同源

      • 页面源和页面运动过程中加载的源不一致也有跨源问题

        • css,js 相同宽松

        • ajax 相当严格,不允许ajax 跨域访问资源

          • 出现跨域问题解决方案

            • 代理

            • JSONP

            • CORS

              • 是基于http1.1的一个跨域解决方案

              • Cross-Origin-Resource-Share

              • CORS规定三种不同的交互模式

                • 简单请求

                  • 请求方式满足

                    • get

                    • post

                    • head

                  • 请求头包含安全字段的

                    • Accept

                    • Accept-Language

                    • Content-Language

                    • Content-Type

                      • text/plain

                      • mutipart/form-data

                      • application/x-www-urlencoded

                    • DPR

                    • Downlink

                    • Save-Data

                    • Viewport-Width

                    • Width

                  • 浏览器判断为简单请求后会发生

                      1. 请求头自动添加Origin
                      1. 服务器响应头中包含access-control-allow-origin
                • 需要预检的请求

                  • 浏览器发送预检请求,询问浏览器是否允许

                    • options 请求

                      • 浏览器会添加三个请求头

                        • Origin

                        • Access-Control--Request-Method

                        • Access-control-Request-Headers: a,b,token,xxxx

                      • 响应头

                        • Access-Control--Request-Methods

                        • Access-control-Request-Headers: a,b,token,xxxx

                        • Access-control-Request-Max-Age

                  • 服务器允许,发送真实请求

                  • 服务器完成响应

                • 需要携带身份凭证的请求

                  • 细分主题 1

    文件上传

    • 消息格式

      • 请求头携带Content-Type

        • 细分主题 1

    浏览器的输入URL 之后会发生什么

      1. 浏览器自动补全协议端口
      1. 浏览器自动完成URL 编码, 转为base64
      1. 浏览器根据url 查找本地缓存
      • 强缓存

        • 根据缓存规则查看是否命中缓存,若命中则直接使用缓存,不再发送请求
      • 协商缓存

        • HTTP 1.1

          • 正常缓存:查看是否过期 Cache-control: max-age=3600 Etag: ABC Data:xxxxxx

            • 协商内容: If-Noe-Match: ABC 响应状态码: 304 或者200
        • HTTP 1.0.

          • 正常缓存:查看是否过期 Expires: Wed,21 Oct 2015 0:30:00 GMT Last-Modified: Wed,21 Oct 2015 0:30:00 GMT

            • 内容协商: If-Modified-Since: Wed,21 Oct 2015 0:30:00 GMT 响应状态码: 304 或者200
          • 缺点:

    1. 用户本地时间修改导致文件过期
    2. 如果文件一秒钟被修改了N次,使用时间点无法判断
      1. 通过DNS解析出服务器的IP 地址
      1. 浏览器向服务发送建立TCP 链接的申请,完成三次握手建立通道连接
      1. 若是使用了HTTPS 协议,则还会有SSL 握手,建立加密信道,使用SSL 握手时会确认是否使用http2
      1. 浏览器决定携带哪些cookie到请求头中
      1. 浏览器自动设置好,请求头,协议版本cookie ,发送 get请求
      1. 服务器收到请求,进入服务器流程,处理完成后,响应一个http报文给浏览器
      1. 浏览器根据协议版本,以及请求头中的connection 字段判断是否是否保留tcp 链接
      • 如果关闭的经历TCP四次挥手
      1. 浏览器根据状态码决定如何处理这次响应
      1. 浏览器根据响应头中的content-type 字段识别响应类型,
      • text/html

        • 对响应提内容进行HTML解析
      • image/png

      • text/css

      1. 浏览器根据响应头的其他内容完成缓存,cookie 设置
      1. 解析HTML,从上到下解析HTML, 遇到外部资源链接(js,css)则进一步请求资源
      1. 解析过程生成DOM 树,SSOM树,然后一边生成,一边把二者合并为渲染树,
      • 随后对渲染树中的那个节点,位置和大小计算,最后把节点交给GPU 绘制到屏幕上
      1. 在解析的过程中还会触发一系列的事件
      • DOM 树完成后触发DOMContentLoaded事件,

      • 所有资源加载完毕触发load事件

    网络攻击手段

    • SCRF

      • cross-site request forgery 跨站请求伪造

        • 利用用户身份信息,伪造用户请求的操作
      • 防御方式

        • 不使用cookie,使用jwt

        • 使用sameSite

        • 细分主题 3

    • XSS

      • Cross Site Scripting 跨站脚本攻击

        • 利用网站漏洞,提交一些脚本内容到服务中,当正常用户访问时脚本自动执行
      • 防御方式

        • 过滤一些危险标签和属性,

        • 编码: 对危险标签进行html 编码

    网络性能优化

    • 优化打包体积

      • 利用一些工具压缩,混淆最终打包代码,减少包提及

        • 路由懒加载:

          • Vue/react中把不同的路由拆分成独立的chunk
        • tree shaking, 树摇摇掉未使用的代码(必须使用es module)

          • 例如: lodash 引入时,引入具体的内容文件,而不是所有的文件
        • 代码分割

          • 将第三方库单独打包成一个chunk,利用浏览器缓存

            • webpack配置:splitChunks:[chunnks:all]
    • 多目标打包

      • 利用一些打包插件,针对不同的浏览器打包出不同的兼容版本,这样一来,每个版本中的兼容代码会大量减少,从而减少包体积
    • 压缩

      • 现在浏览器普遍支持压缩格式,因此服务器的各种文件可以压缩后再响应给客户端,只要解压时间小于优化的传输时间,压缩就是可行的
    • CDN

      • 利用CDN 可以大幅缩减静态资源的访问时间,对于公共库的访问,可以使用出名的CDN资源,这样可以时间跨站点的缓存
    • 缓存

      • 对于除HTML 外的文件资源,开启协商缓存,利用打包工具产生的文件hash 值进行置换缓存
    • HTTP2

      • 开启HTTP2 之后,利用多路复用,头部压缩的特性,充分利用带宽传递大量的文件数据
    • 雪碧图

      • 将多个图片合并为雪碧图
    • defer, async

      • 通过设置script 属性,让页面尽早加载JS

        • defer

        • async:

    • prefetch, preload

      • prefetch

        • 可以让页面在空闲时间与下载其他页面可能用到的资源
      • preload

        • 可以让页面预先下载本页面可能用到的资源

          • 适合首屏幕关键字,关联css,js 等
      • preconnect

        • 提前和第三方域名链接
    • 多个静态资源域

      • 对于不支持http2 的场景, 将相对独立的静态资源分到多个域中报错,可以让浏览器同时开启多个TCP链接,并行下载

    断点续传

    • 原理

        1. 先用head 请求获取响应头中的参数判断是否支持断点续传
        • accept-ranges: bytes

        • content-length:1024

        1. 客户端决定是否使用断点续传,使用时,设置请求头
        • 断点下载(其他桌面应用,浏览器无法完成)

          • Range:bytes=0-512

            • 保存到临时文件中
          • Range:bytes=513-1024

            • 保存到临时文件中
          • 将碎片文件组装成完整文件

        • 断点上传

          • 用户选择文件后,前端将文件切割为多个芬片

          • 得到文件的MD5 和每个分片的MD5

    域名解析的过程

    • 浏览器缓存

    • 系统缓存

    • Hosts文件

    • LDNS 本地DNS扶服务器

    • 根服务器→获得顶级域服务器地址→再问权威服务器→最

    SSL,TLS, HTTPS的关系

    • 解释

      • SSL(Secure Sockets Layer) 安全套接字协议

      • TLS(Transport Layer Security)传输层安全协议

        • TLS 是SSL 的升级版本,两个几乎一样
      • HTTPS 建立在SSL 协议上的http 协议

    • 常见问题

      • 中间人攻击

        • 细分主题 1

    Webpack

    配置

    • 配置参数

      • 配置参数放在命令行中,但是参数较多时,可以放在 webpack.config.js 文件中
    • 配置文件

      • 默认文件 webpack.config.js 可以通过 命令行 --config 指定文件位置

      • 通过 commonJS 导出一个对象

      • 注意: 当命令行和配置文件中的配置冲突时以命令行为准

    • 基本配置

      • mode

        • 编译模式,字符串 development/ production
      • entry

        • 入口, 字符串 指定文件

        • 配置

          • [chunk name]: 入口文件的位置
      • output

        • 出口 指定编译结果文件

        • 属性

          • path: 绝对路径

          • filename: [chunk name].[hash].js

          • library:abc

            • 打包结果中,将会自动执行函数的执行结果暴露给abc
          • libraryTarget: var

              • var 默认

              • windows

              • windows

              • global

              • this

          • target

            • 目标的构建环境

              • web 默认

              • node

          • module.noParse

            • 不解析 某个模块 正则匹配
      • devtool

        • 配置sourse map

          • none

          • eval

          • sourse-map

            • 需要在开发环境中调试源码使用 (需要服务器配置,不允许普通用户访问souurse map 文件)
      • 其他参数

        • content

          • 操作的文件夹路径, 默认命令行当前文件位置
        • resolve

          • .modules

            • 默认值: ['node_modules'] 模块的查找位置 可以追加多个目录
          • extensions

            • 文件的默认扩展名 默认: ['.js',".json"]
          • alias

            • 别名
        • externals

          • 忽略这些包的打包(jquery,loash)

    sourse map

    • 前端发展到现在的工程化后,很多时候不能看直接运行的源代码,可能需要对源代码进行合并,压缩,转换等操作,真正运行的是转换后的代码,不方便调试

    • 为了解决这一问题,chrome 浏览器率先支持sourse map, 其他浏览器纷纷效仿,

    • sourse map 实现上是一个配置,不止记录了源码的内容,还记录了转换后的代码关系

    • 原理

      • 打包后的代码最后一行是,当前文件中 map文件的名称 soursemappingURL=xxxx.map

      • 通过xxx.map 解析出源文件的内容

      • 当运行时,发送错误,浏览器会显示原始代码中的对应的错误位置

    • 最佳实践

      • souse map 应用在开发环境中使用,作为一种调试手段

      • 它不应该在生产环境中实践,sourse map 的文件一般都很大,不仅影响网络传输,还容易暴露原始代码,

    编译过程

    • 初始化

      • 会将cli 参数 , 配置文件参数,默认配置 进行融合, 形成一个最终的配置文件

      • 对配置的处理依托于一个第三方库, yags 完成

    • 编译

      • 创建 chunk

        • chunk 是webpack 内部的一个概念,他表示通过某个入口找到所有依赖的统称

        • 每个chunk 至少有两个属性 name: 默认 main ID: 唯一编号, 开发环境和name 相同,生产环境是一个数字,从0开始

      • 构建依赖

        • 检查是否记录

        • 读取文件内容,语法分析

        • 处理loaders

          • 当前模块是否满足某个规则

          • 读取规则中的loaders 没有复合的返回[]

          • 细分主题 3

        • AST生成语法树,记录到依赖中

        • 保存到 dependencies 中

        • 替换依赖函数

        • 保存转换后的模块代码

      • 产生 chunk assets

        • 通过第二步, chunk 会产生一个模块列表,模块列表包含,模块id 和 模块转换后的代码

        • chunk hash 是根据 chunk assets 中的内容生成的hash zifuchuan

      • 将多个chunk 的asset 合并一起,产生一个总的hash

    loader

    • loader 本质是一个函数,他的作用是将某个源码字符串转换成另一个源码字符串返回

    • 配置代码

      • module.exports = { mode: "development", module: { // 针对模块的配置, 目前版本只有两个配置, rules, noPares rules: [ // 模块匹配规则可以存在多个规则 { // 没个规则是一个对象 test: /index.js$/, // 正则匹配 use: [ { loader: path.resolve("./loaders/test-loader.js"), // loader 模块的路径 options: {}, // 对应loader 传递的额外参数 }, ], }, ], }, };

      • loader 中读取参数 使用 loader-utils 库

      • 传参数方式

        • 通过 options:{ [变量名]: 参数值 }

        • 在loader: /loaders/test-loader.js?[变量名]=参数值

      • 导出函数,的raw 属性, 表示处理,原始数据

    plugin

    • loader 定位是转换代码,而其他操作难以用loader完成,比如:
    1. webpack 生成文件时,顺便多生成一个说明文件

    2. 当webpack 编译启动时,控制台输入一句话

      • 这种类似的功能需要把功能嵌入到webpack 的编译流程中,而这种事情的实现依托于pligin
    • 写法

      • class Myplugin { apply(compiler){

    } }

    Code
    - apply 函数会在初始化阶段,创建好complier 对象后运行 - compiler 如何使用 - compiler.hooks.事件名称.事件类型(name, function(compilation){})
    • 应用:

      • plugins: [new Mypligin()]

    打印输出

    promise

    • Promise.resolve() .then(() => { console.log(0) return Promise.resolve(4) }) .then((res) => console.log(res))

    Promise.resolve() .then(() => console.log(1)) .then(() => console.log(2)) .then(() => console.log(3))

    .then(() => console.log(5)) .then(() => console.log(6))

    Code
    - 123456 - return Promise.resolve(4)

    等价于 Promise.resolve(4)。then().then()

    Nginx

    负载均衡的策略

    • 轮询(默认)

      • 按照每个请求时间顺序分配不同的后端服务器,如果down 掉 会自动剔除

        • upstream backserver { server 192.168.0.14; server 192.168.0.15; }
    • weight

      • 指定轮询记录,weigth 和访问比率成正比,用于后端服务器性能不均的时候

        • upstream backserver { server 192.168.0.14 weight=3; server 192.168.0.15 weight=7; }
    • ip_hash

      • 通过hash 算法,自动定位到一个服务器上,保证后面的访问也达到同一个服务器上

        • upstream backserver { ip_hash; server 192.168.0.14:88; server 192.168.0.15:80; }
    • fair

      • 按后端服务器的响应时间来分配请求,响应时间短的优先分配。

        • upstream backserver { server server1; server server2; fair; }
    • url_hash

      • 按访问url的hash结果来分配请求,使每个url定向到同一个(对应的)后端服务器,后端服务器为缓存时比较有效。

        • upstream backserver { server squid1:3128; server squid2:3128; hash $request_uri; hash_method crc32; }

    WEB3

    链接钱包的方式

    • 浏览器插件

    • 通过钱包app中访问Dapp

    • 通过walletconnect协议建立连接

      • WalletConnect 则是通过服务端中转的方式来实现的(EIP-1328)
    • [Summary] 通过钱包注入到浏览器运行环境中的接口来实现

      • 一种是通过 EIP-1193 来实现的,另一种是通过 EIP-6963 来实现的。EIP-1193 是一个早期的协议

    EIP1193

    • 它定义了在浏览器中如何通过 JavaScript 与钱包进行交互,有了这个规范钱包才能按照他提供相关接口,DApp 才能按照它调用钱包提供的接口

    • 其实就是约定了浏览器运行时的全局对象 window 上的 ethereum 对象的格式,定义了一些方法和事件

    • 缺陷: 就是 window.ethereum 对象只有一个,所以当用户安装了多个钱包时,用户只能选择一个钱包来使用。这样会导致钱包之间会争抢 window.ethereum 对象,一方面损害了用户体验,另外也不利于钱包之间的良性竞争

      • 导致Dapp 需要适配很多钱包,增加了Dapp 的开发成本

    EIP6963

    • 是对EIP1193 的升级

    • 不再通过windows.ethereum 对象来和钱包交互,而是通过windows 发送事件你的方式和钱包交互,从而解决多个钱包可以和 DApp 进行交互,而不会争抢 window.ethereum 对象

    生态

      • 比较有名的链

        • Ethereum

        • Polygon

        • Optimism

        • Arbitrum

        • Solana

        • Avalanche

        • Sui

    • 应用

      • 基于一条或者多条链开发自己的业务逻辑

      • 例如

        • DEX 去中心化交易所

          • 给用户提供资产交换的地方

          • 例如

            • Uniswap

            • Sushiswap

            • Curve

            • PancakeSwap

          • 去中心借贷

            • AAVE

            • Compound

            • MakerDAO

        • NFT 项目

          • NFT 项目

            • 无聊猿(BAYC)

            • Azuki

            • 胖企鹅

          • NFT 市场

            • Opensan

            • Blur

          • 治理

            • Snapshot
          • GameFI, 预言机, 跨链桥

    • 服务

      • 中心化交易所

        • OKX

        • 币安

      • 安全审计

        • 部署前合约审计

        • 例如

          • Certik

          • Slowmist

      • 浏览器

        • Etherscan
      • 钱包

        • 插件

          • Metamask(Ethereum)

          • Phantom( solana)

          • Martin ( Sui & Aptos)

        • 移动端

          • Imtoken
        • 特殊需求

          • Safe 多签

          • Ledger(硬件)

      • 新闻发布

        • 深潮

        • 金财

        • DAO