自己整理准备的面试题,更新中

648 阅读26分钟

一、JS面试题

JS的数据类型有哪些

  • 基本数据类型:Undefined,Boolean,Number,String,Null,Symbol(ES6新增,表示独一无二的值),BigInt

直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。

  • 引用类型值:Object,Array,Function,Date,Math

同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

怎么判断JS的数据类型?有几种方法?

  • typeof [val]:用来检测数据类型的运算符
  • instanceof:用来检测当前实例是否率属于某个类
  • constructor: 基于构造函数检测数据类型(也是基于类的方式)
  • Object.prototype.toString.call():检测数据类型最好的办法 juejin.cn/post/684490…

什么是闭包?

函数执行形成了不能被销毁释放的私有栈内存,但是在项目中不要过多的使用闭包,因为闭包会保留它们包含函数的作用域,所以比其他函数更占用内存。过度使用闭包可能导致内存过度占用,内存溢出。

闭包的作用

  • 保护:私有变量与外界变量没有必然联系,防止了全局变量的污染
  • 保存:形成了不销毁的私有栈内存,私有作用域中的私有变量等信息保存下来

什么是作用域和作用域链?

作用域 指程序中定义变量的区域,函数执行时形成私有作用域,执行上下文

function func () { func 函数作用域开始
    //  函数体
} //  func 函数作用域结束
func(); 

作用域链

上下文中的代码在执行的时候,会创建变量对象的一个作用域链,这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序,私有栈内容中的函数执行时,如果遇到一个变量,首先查找当前私有栈内存中是否有该变量,如果没有向上级作用域查找,一直到全局作用域。

什么是原型和原型链?

原型

每个函数都会创建一个prototype属性,这个属性是一个对象,用来存储实例公用的方法和属性,这个对象就是通过调用构造函数创建的对象的原型;
在prototype对象中,有一个constructor属性,这个属性是函数本身

原型链

每一个引用数据类型都有一个proto属性,这个属性指向所属类的原型prototype

原型链查找机制:在查找方法、属性时,先找自己私有的属性、方法,有则直接调用,没有便基于__proto__查找所属类原型上的方法(Function.prototype)向上查找,一直到原型链的末端Object.prototype,这种链形查找就是原型链查找机制,查找过程中的构造了原型链

原型链潜在问题

  • 1、原型链污染: 如果在原型链上的对象被修改,会影响到所有基于该原型链创建的对象。这可能导致意外的行为,特别是在多个模块或库共同使用时。
  • 2、属性屏蔽: 如果在实例对象上定义了与原型链上相同名称的属性,实例对象上的属性会屏蔽原型链上的属性
  • 3、难以追踪错误来源
  • 4、性能问题: 原型链的查找是一个递归的过程,当链条较长时,可能会导致性能问题。这通常不太明显,但在某些高性能要求的场景下可能需要考虑。
  • 注意使用 hasOwnProperty 来检查属性是否是实例对象自己的属性,而非原型链上的属性。

JS继承

箭头函数和普通函数的区别,匿名函数的this是谁?

1、箭头函数语法更加简洁、清晰
2、箭头函数不会创建自己的this

箭头函数没有自己的this,箭头函数的this指向在定义(注意: 是定义时,不是调用时)的时候继承自外层第一个普通函数的this。所以,箭头函数中 this 的指向在它被定义的时候就已经确定了,之后永远不会改变。

3、call | apply | bind 无法改变箭头函数中this的指向
4、箭头函数不能作为构造函数使用
5、箭头函数不绑定arguments,取而代之用rest参数...代替arguments对象,来访问箭头函数的参数列表
6、箭头函数不能用作Generator函数,不能使用yield关键字

什么是事件循环?

  • js是单线程运行的,在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行
  • 在执行同步代码的时候,如果遇到了异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务
  • 当同步事件执行完毕后,再将异步事件对应的回调加入到与当前执行栈中不同的另一个任务队列中等待执行
  • 任务队列可以分为宏任务对列和微任务对列,当当前执行栈中的事件执行完毕后,js 引擎首先会判断微任务对列中是否有任务可以执行,如果有就将微任务队首的事件压入栈中执行
  • 当微任务对列中的任务都执行完成后再去判断宏任务对列中的任务。

ES6都有哪些新增的方法属性?

  • 新增symbol类型 表示独一无二的值,用来定义独一无二的对象属性名;
  • const/let 都是用来声明变量,不可重复声明,具有块级作用域。存在暂时性死区,也就是不存在变量提升。(const一般用于声明常量);
  • 变量的解构赋值(包含数组、对象、字符串、数字及布尔值,函数参数),剩余运算符(...rest);
  • 模板字符串(${data});
  • 扩展运算符(数组、对象);;
  • 箭头函数;
  • Set和Map数据结构;
  • Proxy/Reflect;
  • Promise;
  • async函数;
  • Class;
  • Module语法(import/export)。
  • Array.isArray()

== 和 === 有哪些区别

== 进行比较时,如果两边的数据类型不一样,则先转换为相同的数据类型,然后再进行比较;
=== 进行比较时,如果数据类型不一样,就直接返回false

  • {} == {}:两个对象进行比较,比较的是队内存地址
  • null == null/undefined:相等,除此之外都不相等
  • NaN和任何数都不相等,包括他自己
  • [1] == '1':对象和字符串比较,是先把对象toString()转换为字符串后进行比较
  • 剩余情况在进行比较的时候,都是转换为数字(前提是数据类型不同)
    • 对象转换为数字:先转化为字符串,然后在转换为数字(普通对象转化字符串为'[object Object]')
    • 字符串转换为数字:只要出现一个非数字字符就是NaN
    • 布尔转换为数字:true => 1;false => 0
    [12] == true => Number([12].toString() == 1) => false
    [] == false => 0 == 0 => true
    [] == 1 => 0 == 1 => false
    '1' == 1 => 1 == 1 => true

call、apply、bind的区别

call方法:(fn.call([context],[params],...))
  • 执行当前的函数
  • 把函数中的this指向改为第一个传递给call的参数,如果不传非严格模式下this的指向是window,严格模式下不传是undefined
  • 把传递给call其余的实参当做参数信息传给当前函数
apply:fn.apply([context],[1,3])
  • call方法一样都是把函数执行,改变函数的this,唯一区别就是apply传参是数组
bind方法
  • bind和call、apply区别是,apply预先改变函数的this指向,并没有执行函数

怎样理解new命令的执行过程

通过new命令生成一个实例对象经历了四个步骤
1、创建一个空对象,作为要返回的实例
2、将空对象的原型指向构造函数的prototype属性,设置原型链
3、将空对象赋值给构造函数的this
4、执行构造函数内部代码

// 执行流程
function newNew (constructor, ...args) {
    // 1. 创建一个新的空对象
    var newObj = {};
    
    // 2. 将新对象的原型设置为构造函数的原型
    newObj.__proto__ = constructor.prototype;
    
    // 3. 改变this指向为新对象,赋值给新对象
    var result = constructor.apply(newObj, args);
    
    // 4. 如果构造函数有显式返回一个对象,则返回该对象;否则返回新对象
    return (result && (typeof result === 'object' || typeof result === 'function')) ?
          result : 
          newObj;
}

数组去重

Array.from与set去重
function unique(ary) {
    return Array.isArray(ary) ? Array.from(new Set(arr)) : ary
}
indexOf方法去重
function unique(ary) {
    let res = [];
    if (Array.isArray(ary)) {
        ary.forEach(item => {
            if (res.indexOf(item) === -1) {
                res.push(item);
            }
        });
    } else {
        res = ary;
    }
    return res;
}
Object的hasOwnProperty对和filter
function unique(ary) {
    let obj = {}
    let res = ary.filter(item => {
        return obj.hasOwnProperty(item) ? false : (obj[item] = true);
    })
    return res;
}

数组扁平化

//=>1、concat/reduce/isArray
function myflat(arr) {
    return arr.reduce((acc, current) => {
        return acc.concat(Array.isArray(current) ? myflat(current) : current);
    }, []);
}
	
//=>2、递归反嵌套
function myflat(ary) {
    let flatAry = [];
    (function flat(ary) {
        ary.forEach(item => Array.isArray(item) ? flat(item) : flatAry.push(item));
    })(ary);
    return flatAry;
}

//=>3、转换字符串
arr = arr.toString().split(',').map(item => Number(item))
}

什么是防抖、节流?实现防抖节流

//=>防抖
function _debounce(func, wait) {
  let timer = null,
    result = null;
  return function anonymous(...args) {
    let context = this;
    clearTimeout(timer);
    timer = setTimeout(() => {
      result = func.call(context, ...args);
      timer = null;
    }, wait);
    return result;
  }
}

//=>节流
function throttle(fun, delay) {
    let last = 0,
    deferTimer = null;
    return function () {
        let that = this
        let _args = arguments
        let now = +new Date()
        if (last && now < last + delay) {
            clearTimeout(deferTimer)
            deferTimer = setTimeout(function () {
                last = now
                fun.apply(that, _args)
            }, delay)
        } else {
            last = now
            fun.apply(that, _args)
        }
    }
}

深克隆和浅克隆的区别,实现一个深克隆

浅拷贝(浅克隆):复制出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。

深拷贝(深克隆):复制出来的所有变量都含有与原来的对象相同的值,那些引用其他对象的变量将指向复制出来的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

//=>1、JSON.parse
JSON.parse(JSON.stringify(oldObj));

//=>2、
function deepClone(obj, clonedObjects = new WeakMap()) {
            // 如果是基本数据类型、函数或者是 null,则直接返回
            if (obj === null || typeof obj !== 'object') {

                if (typeof obj === 'function') {
                    // 如果是函数,直接赋值,不克隆
                    return obj;
                }
                return obj;
            }

            // 检查是否已经克隆过该对象,如果是则直接返回之前的克隆对象
            if (clonedObjects.has(obj)) {
                return clonedObjects.get(obj);
            }

            // 根据对象的类型创建新的对象或数组
            var clone = Array.isArray(obj) ? [] : {};

            // 将新对象添加到已克隆对象的映射中
            clonedObjects.set(obj, clone);
            
            // 递归地克隆对象或数组的每个属性,包括函数
            for (var key in obj) {
                if (obj.hasOwnProperty(key)) {
                    if (typeof obj[key] === 'function') {
                        // 如果属性是函数,直接赋值,不克隆
                        clone[key] = obj[key];
                    } else {
                        // 否则递归地克隆对象的属性
                        clone[key] = deepClone(obj[key], clonedObjects);
                    }
                }
            }
            return clone;
        }

let、const、var的区别

  • let 不存在变量提升(当前作用域中,不能在let声明前使用变量)同一个作用域中,let 不允许重复声明
  • let解决了typeof的一个暂时性死区问题
  • 全局作用域中,使用let声明的变量并没有给window加上对应的属性
  • let会存在块作用域(除对象以外的大括号都可被看做块级私有作用域)

如何实现多个Promise按顺序执行?

let f1 = function() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('promise 1')
            resolve(1);
        })
    })
}

let f2 = function() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('promise 2')
            resolve(2);
        })
    })
}

//  1、使用async await
async function asyncPromise() {
    await f1();
    f2();
}

//  2、使用reduce
let ary = [f1, f2];
function reducePromise(ary, value) {
    //  previous: 上一次返回的数据
    //  current: 数组中正在处理的元素
    //  Promise.resolve(value): 第一次调用`callback`函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。
    return array.reduce((previous, current) => {
        return previous.then(current)
    }, Promise.resolve(value))
}

//  3、递归
let ary = [f1, f2];
function recursionPromise(ary) {
    let promiseItem = art.shift()
    if (promiseItem) {
        promiseItem().then(() => {
            recursionPromise(ary);
        })
    }
}

什么是Promise?我们用Promise来解决什么问题?

Promise 是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。 promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态) ;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • 这个promise可以解决异步的问题,本身不能说promise是异步的

Promise的方法都有哪些?

  • Promise.reject(reason) :方法返回一个带有拒绝原因(reason参数)Promise对象。
    • 参数:reason表示Promise被拒绝的原因。
    • 返回值:一个给定原因了的被拒绝的Promise
  • Promise.resolve(value):静态方法 Promise.resolve返回一个解析过的Promise对象。
    • 参数:将被Promise对象解析的参数,也可以是一个Promise对象,或者是一个thenable。
    • 返回值:返回一个带着给定值解析过的Promise对象,如果参数本身就是一个Promise对象,则直接返回这个Promise对象。
  • Promise.all(iterable):如果全部成功,状态变为 resolved,返回值将组成一个数组传给回调;只要有一个失败,状态就变为 rejected,返回值将直接传递给回调all() 的返回值也是新的 Promise 对象
    • 参数:一个可迭代的对象
  • Promise.race():方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
  • catch():promise错误状态处理
  • then():promise成功状态处理

手写Promise.last

/**
 * @description: Promise.last方法实现
 * @param {array} list - promise组成的数组
 * @return Promise - 最后一个resolve的promise,如果执行完result还是null的话就返回reject
 */
Promise.last = function(list) {
    const len = list.length
    let result = null
    let count = 0
    return new Promise((resolve, reject) => {  
        try {
            for (let i = 0; i < len; ++i) {   
                list[i].then( data => {
                    count++
                    result = data
                    count === len ? resolve(result) : ''
                }, () => {
                    count++
                    count === len ? result === null ? reject(`all promises had been rejected`) : resolve(result): ''
                })
            }
        } catch (e) {
            reject(e)
        }
    })
}

手写一个Promise

如何理解Module

二、HTTP

HTTP知识体系,挺全面的,推荐看看,https://juejin.cn/post/6844904100035821575

HTTP和HTTPS有什么区别

CDN是什么

CDN (全称 Content Delivery Network),即内容分发网络

构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术

简单来讲,CDN就是根据用户位置分配最近的资源

页面加载白屏都是什么原因导致的,如何排查,如何解决

可能的原因:

  1. HTML 结构问题:

    • 缺少必要的 HTML 标签或结构错误导致页面无法正确渲染。
  2. CSS 或 JavaScript 错误:

    • CSS 文件加载失败或包含错误可能导致页面样式错乱或无法显示。
    • JavaScript 错误或死循环可能阻止页面正常渲染。
  3. 资源加载问题:

    • 图片、字体或其他外部资源加载失败导致页面展示不完整。
    • CDN 服务故障或网络问题导致资源无法正常加载。
  4. 网络问题:

    • 客户端或服务器网络连接问题导致页面无法正确加载。

如何排查:

  1. 检查开发者工具:

    • 使用浏览器开发者工具(通常按 F12 键打开)查看控制台、网络、元素等面板,查找可能的错误信息和加载失败的资源。
  2. 查看网络请求:

    • 查看网络面板,检查页面是否成功加载所有资源(如 HTML、CSS、JavaScript、图片等)。
  3. 检查 HTML 结构:

    • 检查页面的 HTML 结构是否正确,确保没有缺少必要的标签或结构错误。
  4. 排查 JavaScript 和 CSS:

    • 检查 JavaScript 和 CSS 文件是否正确加载,以及是否包含错误。可以尝试注释掉部分 JavaScript 或 CSS 代码来排查问题。
  5. 尝试禁用插件和扩展:

    • 有时浏览器插件或扩展可能会影响页面加载,尝试禁用所有插件和扩展再重新加载页面。
  6. 检查服务器状态:

    • 检查服务器是否正常运行,以及网络是否正常连接。

如何解决:

  1. 修复 HTML 结构、JavaScript 和 CSS 错误:

    • 修复 HTML 结构错误,确保页面正确渲染。
    • 修复 JavaScript 和 CSS 错误,确保页面脚本和样式正常加载。
  2. 优化资源加载:

    • 对资源进行压缩、合并、缓存等优化操作,提高页面加载速度。
  3. 使用服务端渲染(SSR):

    • 对于大型单页面应用(SPA),考虑使用服务端渲染,减少客户端渲染的压力,提高页面首次加载速度。
  4. 添加加载指示器:

    • 在页面加载过程中显示加载指示器,让用户知道页面正在加载。
  5. 使用错误监控工具:

    • 集成错误监控工具,及时捕获并修复页面加载过程中出现的错误。
  6. 使用预加载技术:

    • 使用预加载(Preload)或预渲染(Prerender)技术,提前加载页面所需资源,加快页面加载速度。

如果某个地区的服务器快,某个慢是什么原因,怎么解决

可能的原因:

  1. 网络延迟:

    • 不同地区之间的网络延迟可能不同,导致在网络延迟较高的地区访问服务器速度较慢。
  2. 网络拥塞:

    • 某些地区的网络可能存在拥塞问题,导致数据传输速度较慢。
  3. 服务器负载:

    • 某些地区的服务器可能承载的负载较大,导致响应速度较慢。
  4. 带宽限制:

    • 某些地区的服务器可能受到带宽限制,限制了数据传输速度。
  5. 硬件设备性能:

    • 不同地区的服务器可能使用的硬件设备性能不同,导致服务器处理速度不同。

如何解决:

  1. 使用内容分发网络(CDN):

    • 使用 CDN 可以将静态资源缓存到分布在全球各地的服务器上,从而提高全球范围内的访问速度。
  2. 优化网络连接:

    • 可以通过优化网络连接,如选择更快的网络服务提供商、使用专线连接等方式,降低网络延迟和拥塞。
  3. 增加服务器节点:

    • 在访问量较大的地区增加服务器节点,通过增加服务器容量来提高访问速度。
  4. 优化服务器性能:

    • 对服务器进行优化,包括提升硬件性能、优化软件配置、使用高效的算法等,提高服务器的响应速度。
  5. 限制带宽使用:

    • 对服务器的带宽使用进行限制,避免带宽被大量的非必要数据占用,保障关键数据的传输速度。
  6. 监控和调优:

    • 定期监控服务器性能和网络连接情况,发现问题及时调整和优化。
  7. 使用合适的服务器提供商:

    • 选择性能良好、提供全球分布式服务的服务器提供商,确保服务器能够提供稳定快速的服务。

图片加载过慢都有什么解决方式

  1. 优化图片大小:

    • 使用适当的图片尺寸和压缩率,以减小图片文件大小。可以使用图片编辑软件或在线工具对图片进行优化,以确保图片大小合适。
  2. 使用合适的图片格式:

    • 根据图片内容选择合适的图片格式。例如,对于简单的图标或线条图像,使用 PNG8 或 SVG 格式可以获得更小的文件大小;对于照片等复杂图像,使用 JPEG 格式通常更合适。
  3. 延迟加载(Lazy Loading):

    • 使用延迟加载技术,只在用户浏览到图片时才加载图片。这样可以减少页面初始加载时的网络请求数量,提高页面加载速度。
  4. 预加载和预渲染:

    • 对于即将显示在屏幕上的图片,可以使用预加载(Preloading)和预渲染(Prerendering)技术,提前加载图片资源,以缩短用户等待时间。
  5. 使用 CDN 加速:

    • 将图片资源托管到内容分发网络(CDN)上,可以加速图片的传输速度,提高用户访问速度。
  6. 使用缓存策略:

    • 使用合适的缓存策略,将图片资源缓存到用户的本地浏览器或 CDN 节点,减少重复加载图片的次数。
  7. 减少 HTTP 请求:

    • 将多个小图片合并成一张雪碧图(Sprite),或者使用 CSS 图片精灵技术,减少 HTTP 请求次数。
  8. 使用 WebP 格式:

    • 对于支持 WebP 格式的浏览器,可以使用 WebP 格式的图片,它通常比 JPEG 和 PNG 格式具有更高的压缩率和更小的文件大小,提高图片加载速度。
  9. 图片懒加载:

    • 当页面滚动到图片可见区域时再加载图片,而不是一次性加载所有图片。这样可以减少页面初始加载时的资源开销,提高页面加载速度。
  10. 压缩和转换图片:

    • 使用图片压缩工具对图片进行压缩,减小文件大小。还可以将图片转换为适合在 Web 上显示的格式,如将 BMP 转换为 JPEG 或 PNG。

HTTP 报文结构是怎样的?

请求报文:

  • 请求行
    • 请求行包括:请求方法字段URL字段HTTP协议版本字段,它们用空格分隔。例如,GET /index.html HTTP/1.1
  • 请求头部:请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔
    • User-Agent:产生请求的浏览器类型。
    • Accept:客户端可识别的内容类型列表。
    • Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。
  • 空行
  • 请求体:post put等请求携带的数据
响应报文
  • 响应行:由协议版本,状态码和状态码的原因短语组成,例如HTTP/1.1 200 OK
  • 响应头:响应部首组成
  • 空行
  • 响应体:服务器响应的数据

响应部首有哪些

通用首部字段(General Header Fields):请求报文和响应报文两方都会使用的首部
  • Cache-Control  控制缓存 ✨
  • Connection 连接管理、逐条首部 ✨
  • Upgrade  升级为其他协议
  • via 代理服务器的相关信息
  • Wraning 错误和警告通知
  • Transfor-Encoding 报文主体的传输编码格式 ✨
  • Trailer 报文末端的首部一览
  • Pragma 报文指令
  • Date 创建报文的日期
请求首部字段(Reauest Header Fields):客户端向服务器发送请求的报文时使用的首部
  • Accept 客户端或者代理能够处理的媒体类型 ✨
  • Accept-Encoding 优先可处理的编码格式
  • Accept-Language 优先可处理的自然语言
  • Accept-Charset 优先可以处理的字符集
  • If-Match 比较实体标记(ETage) ✨
  • If-None-Match 比较实体标记(ETage)与 If-Match相反 ✨
  • If-Modified-Since 比较资源更新时间(Last-Modified)✨
  • If-Unmodified-Since比较资源更新时间(Last-Modified),与 If-Modified-Since相反 ✨
  • If-Rnages 资源未更新时发送实体byte的范围请求
  • Range 实体的字节范围请求 ✨
  • Authorization web的认证信息 ✨
  • Proxy-Authorization 代理服务器要求web认证信息
  • Host 请求资源所在服务器 ✨
  • From 用户的邮箱地址
  • User-Agent 客户端程序信息 ✨
  • Max-Forwrads 最大的逐跳次数
  • TE 传输编码的优先级
  • Referer 请求原始放的url
  • Expect 期待服务器的特定行为
响应首部字段(Response Header Fields):从服务器向客户端响应时使用的字段
  • Accept-Ranges 能接受的字节范围
  • Age 推算资源创建经过时间
  • Location 令客户端重定向的URI ✨
  • vary  代理服务器的缓存信息
  • ETag 能够表示资源唯一资源的字符串 ✨
  • WWW-Authenticate 服务器要求客户端的验证信息
  • Proxy-Authenticate 代理服务器要求客户端的验证信息
  • Server 服务器的信息 ✨
  • Retry-After 和状态码503 一起使用的首部字段,表示下次请求服务器的时间
实体首部字段(Entiy Header Fields):针对请求报文和响应报文的实体部分使用首部
  • Allow 资源可支持http请求的方法 ✨
  • Content-Language 实体的资源语言
  • Content-Encoding 实体的编码格式
  • Content-Length 实体的大小(字节)
  • Content-Type 实体媒体类型
  • Content-MD5 实体报文的摘要
  • Content-Location 代替资源的yri
  • Content-Rnages 实体主体的位置返回
  • Last-Modified 资源最后的修改资源 ✨
  • Expires 实体主体的过期资源 ✨

HTTP 的请求方法都有哪些?

  • GET: 通常用来获取资源
  • HEAD: 获取资源的元信息
  • POST: 提交数据,即上传数据
  • PUT: 修改数据
  • DELETE: 删除资源(几乎用不到)
  • CONNECT: 建立连接隧道,用于代理服务器
  • OPTIONS: 列出可对资源实行的请求方法,用来跨域请求
  • TRACE: 追踪请求-响应的传输路径

GET 和 POST 有什么区别?

  • 语义区别:get是用来获取数据,post是推送数据
  • 缓存的角度:GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
  • 编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。
  • 参数的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,更适合传输敏感信息。
  • 幂等性的角度,GET幂等的,而POST不是。(幂等表示执行相同的操作,结果也是相同的)
  • TCP的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)

你了解的HTTP的状态码都有哪些,表示什么?

RFC 规定 HTTP 的状态码为三位数,被分为五类:

  • 1xx: 表示目前是协议处理的中间状态,还需要后续操作。
  • 2xx: 表示成功状态。
  • 3xx: 重定向状态,资源位置发生变动,需要重新请求。
  • 4xx: 请求报文有误。
  • 5xx: 服务器端发生错误。

1xx

  • 101 Switching Protocols:在HTTP升级为WebSocket的时候,如果服务器同意变更,就会发送状态码 101。
  • 100 Continue: 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息。

2xx

  • 200 OK:正常返回信息,是见得最多的成功状态码,通常在响应体中放有数据。
  • 201 Created:请求成功并且服务器创建了新的资源。
  • 202 Accepted:服务器已接受请求,但尚未处理。
  • 204 No Content:含义与 200 相同,但响应头后没有 body 数据。
  • 206 Partial Content:顾名思义,表示部分内容,它的使用场景为 HTTP 分块下载和断点续传,当然也会带上相应的响应头字段Content-Range

3xx

  • 301 Moved Permanently:永久重定向,请求的网页已永久移动到新位置。站点再也不用了,应当返回301,这个时候浏览器默认会做缓存优化,在第二次访问的时候自动访问重定向的那个地址。
  • 302 Found:临时性重定向。只是暂时不可用,那么直接返回302即可,和301不同的是,浏览器并不会做缓存优化。
  • 303 See Other:临时性重定向,且总是使用 GET 请求新的 URI。
  • 304 Not Modified:当协商缓存命中时会返回这个状态码。

4xx

  • 400 Bad Request:服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。
  • 401 Unauthorized:请求未授权。
  • 403 Forbidden:这实际上并不是请求报文出错,而是服务器禁止访问,原因有很多,比如法律禁止、信息敏感。
  • 404 Not Found:找不到如何与 URI 相匹配的资源。
  • 405 Method Not Allowed:请求方法不被服务器端允许。
  • 406 Not Acceptable:资源无法满足客户端的条件。
  • 408 Request Timeout:服务器等待了太长时间。
  • 409 Conflict:多个请求发生了冲突。
  • 413 Request Entity Too Large:请求体的数据过大。
  • 414 Request-URI Too Long:请求行里的 URI 太大。
  • 429 Too Many Request:客户端发送的请求过多。
  • 431 Request Header Fields Too Large:请求头的字段内容太大。

5xx

  • 500 Internal Server Error:最常见的服务器端错误。
  • 501 Not Implemented:表示客户端请求的功能还不支持。
  • 502 Bad Gateway:服务器自身是正常的,但访问的时候出错了,啥错误咱也不知道。
  • 503 Service Unavailable:服务器端暂时无法处理请求(可能是过载或维护)
  • 504 Gateway timeout:网关超时

4、如何理解 HTTP 缓存及缓存代理?

6、浏览器从输入地址到页面经历了哪些过程

一、浏览器(客户端)进行URL地址解析

URl是URN和URI的子集,一个完整的URL包含

  • URL
  • 1、浏览器(客户端)进行URL地址解析。
  • 2、将解析出的域名进行DNS解析。
  • 3、和服务器建立TCP链接(三次握手)
  • 4、把客户端信息传递给服务器(发送HTTP请求)
    • 请求报文:所有经过传输协议,客户端传递给服务器的内容,都被称为请求报文
    • 响应报文:所有经过传输协议,服务器返回给客户端的内容,都被称为响应报文
    • HTTP报文:请求报文+响应报文
    • 强缓存和协商缓存
  • 5、服务器得到并处理请求(HTTP响应内容),等待服务器响应。
  • 6、客户端渲染服务器返回的内容
  • 7、与服务器断开TCP连接(四次挥手)
  1. 地址解析: 用户在浏览器地址栏中输入一个网址,浏览器首先进行地址解析,将用户输入的域名解析为对应的 IP 地址。这一步通常包括 DNS 查询、本地缓存查询等。

  2. 建立连接(TCP 握手): 浏览器通过解析得到的 IP 地址与服务器建立 TCP 连接。这涉及到一个三次握手的过程,确保客户端和服务器之间建立可靠的连接。

  3. 发送 HTTP 请求: 一旦建立了连接,浏览器就会发送一个 HTTP 请求给服务器,请求特定的资源,比如 HTML 文件、CSS 文件、JavaScript 文件等。

  4. 服务器处理请求: 服务器收到浏览器的请求后,会根据请求的资源进行处理。这可能涉及到后端的业务逻辑、数据库查询等操作。

  5. 返回 HTTP 响应: 服务器处理完请求后,会返回一个 HTTP 响应给浏览器。响应中包含了请求资源的内容,以及一些与响应相关的信息,如状态码、响应头等。

  6. 浏览器解析响应: 浏览器收到响应后,开始解析响应内容。对于 HTML 文件,浏览器会构建 DOM 树;对于 CSS 文件,会构建 CSSOM 树;对于 JavaScript 文件,会执行其中的脚本。

  7. 构建渲染树(Render Tree): 浏览器会将 DOM 树和 CSSOM 树结合起来,构建渲染树。渲染树只包含页面上需要渲染的节点,如元素和文本。

  8. 布局(Layout): 渲染树构建完成后,浏览器进行布局过程,确定每个节点在页面上的位置。这一阶段也被称为回流(Reflow)。

  9. 绘制(Painting): 浏览器根据渲染树和布局信息进行绘制,将页面渲染到屏幕上。这一阶段也被称为重绘(Repaint)。

  10. 显示页面: 最后,浏览器将渲染好的页面显示给用户。

三、HTML、CSS

如何水平垂直居中?

推荐文章:juejin.cn/post/684490…

3、CSS3新特性

  • @font-face 字体图标
  • CSS3选择器
  • 常用样式属性:文字和边框的处理
  • 背景的处理
    • 渐变色背景
    • 背景图片处理
    • 滤镜功能
  • 变形和动画
    • transform
    • transition
    • animation
    • 3D变形动画
  • 盒子模型
    • box-sizing
    • column
    • flex box .
  • 媒体适配和响应式布局开发
  • 兼容处理和prefixfree.min.js

什么是语义化标签,为什么用它?

HTML5语义化顾名思义,指的是合理正确的使用语义化的标签来创建页面结构,如headernavmainarticlesectionasidefooter,从标签上即可以直观的知道这个标签的作用,而不是滥用div。

语义化的优点有:

  • 代码结构清晰,易于阅读,利于开发和维护
  • 方便其他设备解析(如屏幕阅读器)根据语义渲染网页
  • 有利于搜索引擎优化(SEO),搜索引擎爬虫会根据不同的标签来赋予不同的权重

盒子模型

可以认为每个html标签都是一个方块,然后这个方块又包着几个小方块,如同盒子一层层的包裹着,这就是所谓的盒子模型。盒子模型分为IE盒模型W3C标准盒模型

  • W3C 标准盒模型:css设置标准盒子模型属性box-sizing:content-box;。属性width、height只包含内容content,不包含borderpadding
  • IE 盒模型:css设置 IE 盒子模型属性box-sizing:border-box;。属性width、height包含borderpadding,指的是content+padding+border

什么是BFC?如何创建BFC?

position都有哪些属性

  1. static 默认值,元素在文档流中正常排列,不会被定位,toprightbottomleftz-index 属性对其不起作用。
  2. relative 元素相对于其正常位置进行定位,但仍然保留了在文档流中的位置。设置了 toprightbottomleft 属性的值会使元素偏离正常位置,但不影响其他元素的布局。
  3. absolute 元素相对于其第一个非 static 定位的父元素进行定位。如果没有父元素,则相对于 <html> 元素定位。绝对定位的元素不会在文档流中占据空间,因此其定位不会影响其他元素的布局。
  4. fixed 元素相对于浏览器窗口进行定位,即使页面滚动,元素也会固定在相对于视口的位置。固定定位的元素不会在文档流中占据空间,因此其定位也不会影响其他元素的布局。
  5. sticky 元素基于用户的滚动位置在容器中定位,但它表现为在滚动容器的范围内是相对定位,而在滚动容器之外是固定定位。这意味着元素在其父元素内滚动时,将保持其定位,直到达到指定的偏移位置,此时它将固定在父元素的指定位置。

怎么理解弹性布局

flex布局都有哪些属性,flex属性都有哪些参数都是什么意思

  1. 容器属性(作用于父元素):
    • display 定义容器是一个弹性容器。

      • flex:创建一个弹性容器。
    • flex-direction 定义主轴的方向。

      • row:主轴为水平方向,起点在左端。
      • row-reverse:主轴为水平方向,起点在右端。
      • column:主轴为垂直方向,起点在上方。
      • column-reverse:主轴为垂直方向,起点在下方。
    • flex-wrap 定义弹性容器的换行方式。

      • nowrap:默认值,不换行。
      • wrap:换行,第一行在上方。
      • wrap-reverse:换行,第一行在下方。
    • flex-flow flex-directionflex-wrap 的简写属性。

    • justify-content 定义项目在主轴上的对齐方式。

      • flex-start:默认值,项目向主轴起点对齐。
      • flex-end:项目向主轴末尾对齐。
      • center:项目居中对齐。
      • space-between:项目均匀分布在主轴上。
      • space-around:项目均匀分布在主轴上,两端留有一半的空间。
    • align-items 定义项目在交叉轴上的对齐方式。

      • flex-start:项目向交叉轴起点对齐。
      • flex-end:项目向交叉轴末尾对齐。
      • center:项目居中对齐。
      • baseline:项目在交叉轴上以第一行文字的基线对齐。
      • stretch:默认值,项目在交叉轴上拉伸填满容器的高度。
    • align-content 定义多根轴线的对齐方式,仅在一根轴线上有剩余空间时生效。

      • flex-start:多根轴线向交叉轴起点对齐。
      • flex-end:多根轴线向交叉轴末尾对齐。
      • center:多根轴线居中对齐。
      • space-between:多根轴线均匀分布在交叉轴上。
      • space-around:多根轴线均匀分布在交叉轴上,两端留有一半的空间。
  2. 项目属性(作用于子元素):
    • order 定义项目的排列顺序。数值越小,排列越靠前,默认为 0。
    • flex-grow 定义项目的放大比例,默认为 0,即不放大。
    • flex-shrink 定义项目的缩小比例,默认为 1,即有空间时缩小。
    • flex-basis 定义项目占据主轴空间的初始大小。
    • flex flex-grow, flex-shrink, flex-basis 的简写属性。
    • align-self 定义单个项目在交叉轴上的对齐方式,覆盖容器属性 align-items
      • auto:继承父元素的 align-items 属性。
      • flex-start:项目向交叉轴起点对齐。
      • flex-end:项目向交叉轴末尾对齐。
      • center:项目居中对齐。
      • baseline:项目在交叉轴上以第一行文字的基线对齐。
      • stretch:项目在交叉轴上拉伸填满容器的高度。

CSS动画都有哪些

CSS函数是什么,如何使用

  1. rgb()rgba()hsl()hsla() 用于定义颜色值,可以指定红、绿、蓝(或色调、饱和度、亮度)以及可选的 alpha 通道值。
cssCopy code
.background {
  background-color: rgb(255, 0, 0); /* 红色 */
  color: rgba(0, 0, 255, 0.5); /* 半透明的蓝色 */
}
  1. url() 用于引用外部资源,例如图像或字体文件。
cssCopy code
.background {
  background-image: url('background.jpg');
}
  1. calc() 用于执行数学运算,可以在 CSS 中动态计算属性值。
cssCopy code
.width {
  width: calc(100% - 20px);
}
  1. var() 用于使用 CSS 变量的值。
cssCopy code
:root {
  --main-color: #ff0000;
}

.background {
  background-color: var(--main-color);
}
  1. linear-gradient()radial-gradient() 用于创建渐变效果。
cssCopy code
.background {
  background-image: linear-gradient(to right, red, blue);
}
  1. clamp()min()max() 用于设置一个属性的取值范围。
cssCopy code
.width {
  width: clamp(200px, 50%, 500px);
}
  1. attr() 用于获取 HTML 元素的属性值。
cssCopy code
.link::after {
  content: attr(href);
}

CSS选择器都有什么,他们的权重

  • !important: 无穷大,0
  • style行内样式: 1,0,0,0,1
  • ID选择器: 0,1,0,0,2
  • 类,伪类,结构伪类,属性选择器: 0,0,1,0,3
  • 标签、伪元素选择器: 0,0,0,1,4
  • 通配符选择器: 0,0,0,0,5
  • 继承样式: 没有权重,6

CSS如何将超出的文字...省略

.text-ellipsis {
    white-space: nowrap; /* 防止文本换行 */
    overflow: hidden; /* 隐藏溢出的文本 */
    text-overflow: ellipsis; /* 显示省略号 */
}

文字换行如何设置其他行的颜色等样式

三列布局

四、Vue相关

介绍Vue(MVVM,渐进式,vue全家桶)

vue全家桶

  • vue:基础模块(基础语法、核心实现、组件开发、相关指令等)
  • vue-router:构建SPA单页面应用的路由
  • vuex:公共状态管理
  • vue-cli:vue脚手架
  • components:vue element、iview、vux...

渐进式框架

渐进式:类库或者框架都是重量级的,里面包含很多方法,在实际开发我们有可能不是全部用到,所以在框架开发时,把功能按照模块进行单独开发,使用者可根据需求导入对应的模块使用

MVVM/MVC框架

MVVM是双向数据绑定的:VUE本身实现了数据和视图的相互监听影响

MVC是单向数据绑定,数据更改可以渲染视图,但视图更改,对应的数据没有更改,需要自己在控制层基于change事件实现数据的更改(REACT)

  • m:mode 数据层
  • v:view 视图层
  • vm:viewModel 数据和视图的监听层,当数据或者视图发生改变,VM层会监听到,同时把对应的另外一层改变或者重新渲染(Vue就是VM监听层)
    • 数据层改变:vm会帮我们重新渲染视图
    • 视图层改变:vm也会帮我们把数据重新更改

Vue生命周期

每个Vue实例在创建时都会经过一系列的初始化过程,vue的生命周期钩子,就是说在达到某一阶段或条件时去触发的函数,目的就是为了完成一些动作或者事件

Vue2生命周期

  • create阶段:vue实例被创建
    • beforeCreate: 创建前,此时data和methods中的数据都还没有初始化;
    • created: 创建完毕,data中有值,未挂载
  • mount阶段: vue实例被挂载到真实DOM节点
    • beforeMount:可以发起服务端请求,获取数据
    • mounted: 此时可以操作Dom
  • update阶段:当vue实例里面的data数据变化时,触发组件的重新渲染
    • beforeUpdate:组件重新渲染前
    • updated:组件重新渲染后
  • destroy阶段:vue实例被销毁
    • beforeDestroy:实例被销毁前,此时可以手动销毁一些方法
    • destroyed:组件销毁后

vue3生命周期

  • beforeCreate -> 使用 setup()
  • created -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

keep-alive

作用:实现组件缓存
原理:Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。

钩子函数:

  • activated:缓存组件渲染后调用 -> onActivated(vue3)
  • deactivated:缓存组件销毁后调用 -> onDeactivated(vue3)

配置属性

  • include:字符串或正则表达式。只有名称匹配的组件会被缓存
  • exclude:字符串或正则表达式。任何名称匹配的组件都不会被缓存
  • max:数字、最多可以缓存多少组件实例

v-for中key的作用

  • key主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改、复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
  • 有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
实现 DOM diff 算法
  1. 创建JS对象模拟DOM(虚拟DOM)
  2. 把此虚拟DOM转成真实DOM并插入页面中(render)
  3. 如果有事件发生修改了虚拟DOM,比较两棵虚拟DOM树的差异,得到差异对象(diff)
  4. 把差异对象应用到真正的DOM树上(patch)

常用的Vue指令

  • v-if:判断是否渲染;
  • v-show:判断是否显示
  • v-for:数据循环;
  • v-bind:: 缩写,绑定一个属性;
  • v-model:实现双向绑定
  • v-on:@ 缩写,绑定事件

Vue组件之间的通讯

  • props / $emit 适用 父子组件通信
  • ref 与 parent/children 适用 父子组件通信
  • EventBus (emit/on) 适用于 父子、隔代、兄弟组件通信
  • provide / inject 和 attrs/listeners 适用于 隔代组件通信
  • Vuex 适用于 父子、隔代、兄弟组件通信,因为涉及多级隔代传输业务逻辑肯定相对复杂,所有我一般使用Vuex来处理

vuex (是什么,用过吗)

  • vuex 就是一个仓库,仓库里放了很多对象。其中 state 就是数据源存放地,对应于一般 vue 对象里面的 data
  • state 里面存放的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据发生改变,依赖这相数据的组件也会发生更新
  • 它通过 mapState 把全局的 state 和 getters 映射到当前组件的 computed 计算属性
Vuex有5种属性: 分别是 state、getter、mutation、action、module;
  • state:Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据
  • mutations:定义的方法动态修改Vuex 的 store 中的状态或数据
  • actions:可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action
  • getters:类似vue的计算属性,主要用来过滤一些数据
  • 总结

vuex 一般用于中大型 web 单页应用中对应用的状态进行管理,对于一些组件间关系较为简单的小型应用,使用 vuex 的必要性不是很大,因为完全可以用组件 prop 属性或者事件来完成父子组件之间的通信,vuex 更多地用于解决跨组件通信以及作为数据中心集中式存储数据

Vue 是如何实现数据双向绑定的?

Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据
  • 其中,View 变化更新 Data ,可以通过事件监听的方式来实现,所以 Vue 的数据双向绑定的工作主要是如何根据 Data 变化更新 View。我们会通过实现以下 4 个步骤,来实现数据的双向绑定:
  1. 实现一个监听器 Observer ,用来劫持并监听所有属性,如果属性发生变化,就通知订阅者;

Observer 数据监听器,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者,内部采用Object.defineProperty的getter和setter来实现。

  1. 实现一个订阅器 Dep,用来收集订阅者,对监听器 Observer 和 订阅者 Watcher 进行统一管理;

Dep 消息订阅器,内部维护了一个数组,用来收集订阅者(Watcher),数据变动触发notify 函数,再调用订阅者的 update 方法。

  1. 实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图;

Watcher 订阅者, 作为连接 Observer 和 Compile 的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数。

  1. 实现一个解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化

Compile 指令解析器,它的作用对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数。

VueX和pinia的区别和原理

Vue-router的原理

Vue Router 的原理是基于浏览器的 history API 或 hash 模式,监听 URL 变化并根据路由配置进行匹配和渲染相应的组件,同时提供了丰富的导航守卫和钩子函数来实现路由拦截、页面状态管理等功能。

  1. Router 实例化: 在 Vue 应用中引入 Vue Router,并创建一个 Router 实例。在创建实例时,需要传入一个路由配置对象,该对象包含应用的路由信息,包括路由路径、组件映射关系等。
  2. 路由注册: 在应用的根组件中,通过 Vue Router 提供的组件 <router-view> 和指令 <router-link> 来注册路由的入口和链接。<router-view> 会根据当前路由的路径,动态渲染匹配的组件,而 <router-link> 则用于生成页面链接,点击链接时会触发路由导航。
  3. 路由匹配: 当用户触发页面跳转或浏览器 URL 发生变化时,Vue Router 根据当前 URL 和路由配置进行路由匹配。它会遍历路由配置对象,寻找与当前 URL 匹配的路由记录,并确定需要渲染的组件。
  4. 路由导航: 在确定了要渲染的目标组件后,Vue Router 将触发路由导航过程。这个过程包括一系列的导航守卫(beforeEach、beforeResolve、afterEach 等)的执行,这些导航守卫可以用于进行路由拦截、权限控制、页面加载状态管理等操作。
  5. 组件渲染: 导航成功后,Vue Router 将根据匹配到的目标组件,将其渲染到 <router-view> 中,展示给用户。同时,还会触发一些钩子函数(如 beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave)来进行组件的生命周期管理和数据准备。
  6. URL 更新: 如果用户使用了浏览器的前进、后退按钮或直接在地址栏中输入 URL 来触发路由变化,Vue Router 会监听到 URL 的变化,并进行相应的路由匹配和导航,实现页面的无刷新跳转。

hash模式和sistory模式的区别

  1. URL 格式:

    • Hash 模式(Hash mode): URL 中带有 # 符号,例如 http://example.com/#/home# 后面的内容被称为 hash(哈希),用于标识路由路径。浏览器在接收到带有 hash 的 URL 后,不会向服务器发起请求,而是根据 hash 的变化来触发前端路由。
    • History 模式(History mode): URL 中不带 # 符号,例如 http://example.com/home。利用 HTML5 的 history API,在不刷新页面的情况下,实现前端路由的跳转。需要注意的是,在使用 history 模式时,需要服务器端配置来支持在前端路由刷新时,正确返回对应的页面。
  2. 浏览器兼容性:

    • Hash 模式: 对于老版本的浏览器,支持较好,因为 hash 在 URL 中的变化不会导致页面的刷新,所以即使浏览器不支持 history API,也可以实现前端路由。
    • History 模式: 需要浏览器支持 HTML5 的 history API,对于较老的浏览器可能存在兼容性问题。此外,因为 history 模式不会在 URL 中添加 # 符号,因此相对来说更加美观。
  3. SEO 和服务端配置:

    • Hash 模式: hash 的变化不会触发浏览器向服务器发起请求,因此对搜索引擎的爬虫不友好。但是由于 hash 的变化可以通过 JavaScript 监听到,因此可以通过预渲染等技术解决一部分 SEO 问题。
    • History 模式: 由于 history 模式下的路由变化会向服务器发起请求,因此对搜索引擎的爬虫更加友好。但是需要在服务器端配置,以确保在前端路由刷新时,能够正确返回对应的页面。

Router和Route的区别

Router 是整个路由系统的核心管理实例,负责配置和管理路由的各种操作;而 Route 则是每个具体路由导航的表示,包含了导航的相关信息。在 Vue 应用中,我们通过配置 Router 来定义整个应用的路由规则,在组件内部可以通过 Route 对象获取当前导航的信息,并根据需要做出相应的响应。

  1. Router:

    • Router 是 Vue Router 的核心实例,用于管理整个路由系统。
    • 在 Vue 应用中,通过创建一个 Router 实例来启用路由功能,通常在主入口文件中进行配置和实例化。
    • Router 实例负责管理整个应用的路由配置、路由匹配和导航跳转等操作。
  2. Route:

    • Route 是指路由的一条记录,用于表示当前导航的状态。
    • 在 Vue Router 中,每个路由匹配到的组件都会被渲染为一个 Route 实例。
    • Route 对象包含了当前导航的相关信息,如路径、参数、查询参数、元数据等。

Vue2和Vue3都有哪些区别

Vue懒加载异步加载

计算属性和监听器的区别

了解vite吗,为什么编译快?

协商缓存和强缓存

协商缓存和强缓存都是通过http响应头中的字段来实现的

强缓存

强缓存通过在响应头中设置cache-control或expiress字段控制,它告诉浏览器在一定时间内可以直接使用本地缓存不需请求服务器

属性

  1. cache-control:
  • max-age: 指定资源在本地缓存的最大有效时间,时间单位是秒
  • s-maxagemax-age 类似,但只对共享缓存(例如 CDN 缓存)生效。
  • public 表示响应可以被任何中间缓存(如 CDN)缓存。
  • private 表示响应只能被终端用户的浏览器缓存。
  • no-store 禁止缓存,每次都需要向服务器请求资源
// 缓存最大时间是3600秒,可以被任何中间缓存
Cache-Control: max-age=3600, public
  1. expires:
  • 指定缓存资源的过期时间,是一个具体的日期和时间
Expires: Wed, 21 Oct 2022 07:28:00 GMT

协商缓存

协商缓存通过在响应头中设置 ETagLast-Modified 字段,以及相应的请求头字段来控制。它告诉浏览器在一定时间内可以使用本地缓存,但需要向服务器验证缓存的新鲜度。

属性

响应头
  1. ETag:
  • 服务器返回的资源标识符,是一个字符串。浏览器可以在后续请求中通过 If-None-Match 请求头将该值发送给服务器,服务器根据是否匹配返回 304 Not Modified 或完整的资源。
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
  1. Last-Modified:
  • 指示资源的最后修改时间。浏览器可以在后续请求中通过 If-Modified-Since 请求头将该值发送给服务器,服务器根据是否早于资源的最后修改时间返回 304 Not Modified 或完整的资源。
Last-Modified: Wed, 21 Oct 2022 07:28:00 GMT
请求头
  1. If-None-Match:
  • 匹配服务器返回的 ETag 值,如果匹配,则表示缓存仍然有效。
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
  1. If-Modified-Since:
  • 匹配服务器返回的资源的最后修改时间,如果时间早于服务器上的资源修改时间,则表示缓存仍然有效。
If-Modified-Since: Wed, 21 Oct 2022 07:28:00 GMT

get和post区别,他们的缓存有什么区别?

GET 和 POST 是 HTTP 协议中最常用的两种请求方法

1、数据传输方式

GET:

  • 通过 URL 的查询字符串传递数据,数据会附加在 URL 后面,可见于 URL 地址栏。
  • 对数据长度有限制,因为 URL 长度存在限制。
  • 主要用于获取数据,不应该用于敏感数据的传输。

POST:

  • 将数据放在请求体中传输,不会暴露在 URL 中。
  • 可以传输大量数据,理论上没有长度限制。
  • 主要用于提交数据,适合传输敏感数据。

缓存机制:

GET 缓存:

  • GET 请求可以被缓存,默认情况下浏览器会对 GET 请求的响应进行缓存。
  • 如果请求的资源在缓存中存在且缓存有效,浏览器会直接使用缓存,不会再向服务器发起请求。

POST 缓存:

  • POST 请求不会被浏览器缓存,默认情况下浏览器不会对 POST 请求的响应进行缓存。
  • 即使服务器返回了可缓存的响应头,浏览器也不会缓存 POST 请求的响应。

如何取消静态文件缓存

服务端解决方法

  1. 缓存控制头:
  • 在 HTTP 响应头中使用 Cache-ControlExpires 头来控制静态资源的缓存。通过设置适当的值,可以指定缓存的有效期,或禁用缓存no-store

    ```
    Cache-Control: max-age=3600  // 设置缓存有效期为 1 小时
    
    Expires: Wed, 21 Oct 2022 07:28:00 GMT  // 设置缓存过期时间
    ```
    
  1. 版本号或摘要哈希:
  • 在静态资源的 URL 中添加版本号或哈希值。每当静态资源更新时,URL 发生变化,强制浏览器重新下载新的资源。

    ```
    <link rel="stylesheet" href="/css/style-v2.css">
    
    <link rel="stylesheet" href="/css/style.css?v=2a43b5f">
    ```
    

客户端解决方法:

开发时如果有缓存可以用手动清除的方法,或在控制台中网路里将停用缓存勾选

  1. 清除缓存
  2. 强制刷新

webpack和vite有什么区别,他们的原理都是什么

webpack

  1. 打包方式: webpack使用单一的jS文件作为入口,通过配置文件(webpack.config.js)定义各种模块和加载器,然后进行整体打包,webpack通过插件机制支持各种功能扩展
  • entry: webpack的构建入口,可以多入口
// 单入口 
module.exports = {  
    entry: './src/main.js' 
} 
// 多入口 
module.exports = {   
    entry: {    
        a: './src/a.js',    
        b: './src/b.js'  
    } 
}
  • output: 告诉webpack在哪里输入打包的文件,以及如何命名等
module.exports = {
    output: {
        // 通过占位符确保文件名唯一,考虑缓存问题,还可以为文件名加上hash
        filename: '[name].[hash:6].js', 
        path: __dirname + '/dist',
        publicPath: '/',    // 生产环境一般是CDN地址,开发环境配置为/或不配置
    }
}
  • loader: webpack只能理解JS和JSON文件,其他css、图片、sass、ts等不能编译,rules配置对应的loader进行编译处理
  • plugin: 加载插件,配置插件,提供额外的功能和优化。
  1. 热模块替换(HMR): Webpack 提供了 HMR 功能,允许在开发时无需刷新整个页面,只替换发生变化的模块。
devServer: {         
    hot: true,        
    open: true    
}
  1. 代码分割: Webpack 支持代码分割,可以将代码分割成多个小块,按需加载。
  2. 加载器: Webpack 使用加载器来处理非 JavaScript 文件,例如将 CSS 转换为 JavaScript 可以处理的模块。

Vite:

  1. ESM 预构建: Vite 使用原生的 ES 模块(ESM)作为开发时的模块系统,利用浏览器对 ESM 的支持,无需进行整体打包,而是按需加载模块。

  2. 快速开发服务器: Vite 使用一个基于原生 ES 模块的开发服务器,该服务器提供 HMR 功能,能够在开发时快速响应文件变化,实现更快的开发体验。

  3. 适用于小型项目: Vite 的设计目标是在小型项目中实现更快的开发构建速度。

  4. root 指定项目的根目录,默认为当前工作目录。

    
    export default {
      root: './my-app',
    };
    
  5. base 指定项目的基础路径,用于处理静态资源路径。

    export default {
      base: '/my-app/',
    };
    
  6. publicDir 指定静态资源的存放目录,默认为 public

    export default {
      publicDir: 'static',
    };
    
  7. server 配置开发服务器。

    export default {
      server: {
        port: 3000,
        host: 'localhost',
      },
    };
    
  8. proxy 配置代理。

    export default {
      server: {
        proxy: {
          '/api': {
            target: 'http://api.example.com',
            changeOrigin: true,
            rewrite: (path) => path.replace(/^/api/, ''),
          },
        },
      },
    };
    
  9. plugins 配置 Vite 插件。

    import Vue from '@vitejs/plugin-vue';
    
    export default {
      plugins: [
        Vue(),
        // 其他插件
      ],
    };
    
  10. build 配置生产构建选项。

    export default {
      build: {
        outDir: 'dist',
        assetsDir: 'assets',
        // 其他构建选项
      },
    };
    
  11. resolve 配置模块解析。

    export default {
      resolve: {
        alias: {
          '@': '/src',
        },
      },
    };
    
  12. css 配置样式相关选项。

    export default {
      css: {
        preprocessorOptions: {
          scss: {
            additionalData: '@import "@/styles/variables.scss";',
          },
        },
      },
    };
    

如何写一个虚拟滚动列表

cloud.tencent.com/developer/a…

写一个二分法查找

function binarySearch(arr, target) {
  let start = 0;
  let end = arr.length - 1;

  while (start <= end) {
    let mid = Math.floor((start + end) / 2);

    // 如果目标元素等于中间元素,则找到目标元素,返回索引
    if (arr[mid] === target) {
      return mid;
    }

    // 如果目标元素小于中间元素,则在前半部分继续查找
    if (target < arr[mid]) {
      end = mid - 1;
    }
    // 如果目标元素大于中间元素,则在后半部分继续查找
    else {
      start = mid + 1;
    }
  }

  // 如果找不到目标元素,则返回 -1
  return -1;
}

冒泡排序

function bubbleSort(arr) {
  const n = arr.length;

  // 外层循环控制需要进行多少轮排序
  for (let i = 0; i < n - 1; i++) {
    // 内层循环执行一轮冒泡,将当前未排序部分的最大元素“浮”到最右端
    for (let j = 0; j < n - 1 - i; j++) {
      // 如果相邻元素顺序不正确,则交换它们的位置
      if (arr[j] > arr[j + 1]) {
        // 使用解构赋值交换两个元素的位置
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }

  return arr;
}

拖拽定位计算

  1. 记录鼠标按下的初始状态(鼠标位置和被拖拽元素的位置等信息),记录鼠标已按下
  2. 监听鼠标移动,计算拖动距离,更新拖拽位置(初始位置+偏移量)
  3. 清理状态,处理之前记录的初始状态,拖拽结束
  4. 如果有边界设定,则计算不能大于小于对应边界位置

body加入模态框,按浏览器返回键会发生什么,怎么解决

有可能模态框仍留在页面

  1. 监听浏览器状态变化,popstate 监听浏览器返回时,关闭模态框
  2. 当vue和react生命周期结束,关闭使用模态框的组件时,关闭模态框
  3. 也可以监听路由,路由改变,关闭模态框

HLS直播流

  1. 切片(Segmentation): 媒体文件被切分成小的独立的片段(Segment),每个片段通常持续几秒钟。这样可以更好地适应网络状况和不同设备的处理能力。
  2. 播放列表(Playlist): 服务器生成一个包含多个切片的播放列表(Playlist),客户端通过定期请求这个播放列表来获取媒体数据。
  3. 自适应码率调整: 在播放列表中,不同码率的切片都会被提供,客户端根据网络状况自动选择适应的码率,以确保较好的播放质量。
  4. HTTP 协议: HLS 使用 HTTP 协议进行传输,因此能够穿越防火墙和代理服务器,并与现有的基础设施兼容。
  5. 地址是m3u8格式的文件,浏览器的video标签不支持,因为 M3U8 是一种播放列表文件,它本身并不包含音视频内容,而是指向具体的媒体片段。,可以用HLS.js插件解析地址进行播放

前端架构分层

js驱动

什么是面向对象

jsx是用的是什么数据结构,常用的数据结构有哪些

  1. 数组(Array):

    • 由相同类型的元素按顺序组成的集合,可以通过索引访问元素。
  2. 链表(Linked List):

    • 由节点组成的序列,每个节点包含数据和指向下一个节点的指针。
  3. 栈(Stack):

    • 一种先进后出(FILO)的数据结构,只允许在栈顶进行插入和删除操作。
  4. 队列(Queue):

    • 一种先进先出(FIFO)的数据结构,允许在队尾进行插入操作,在队头进行删除操作。
  5. 树(Tree):

    • 由节点和边组成的层次结构,每个节点最多有一个父节点和多个子节点。
  6. 图(Graph):

    • 由顶点和边组成的集合,顶点之间通过边相连。
  7. 堆(Heap):

    • 一种特殊的树结构,通常用于实现优先队列等数据结构。
  8. 哈希表(Hash Table):

    • 一种通过哈希函数将键映射到值的数据结构,支持高效的插入、删除和查找操作。
  9. 集合(Set):

    • 一种不允许重复元素的数据结构,通常用于存储唯一值的集合。
  10. 映射(Map):

    • 一种键值对(key-value)的数据结构,通过键来查找对应的值。
  11. 栈(Stack):

    • 一种后进先出(LIFO)的数据结构,只允许在栈顶进行插入和删除操作。
  12. 队列(Queue):

    • 一种先进先出(FIFO)的数据结构,允许在队尾进行插入操作,在队头进行删除操作。
  13. 双端队列(Deque):

    • 允许在两端进行插入和删除操作的数据结构,结合了栈和队列的特点。
  14. 优先队列(Priority Queue):

    • 一种元素具有优先级的队列,每次取出的元素是具有最高优先级的元素。
  15. 二叉树(Binary Tree):

    • 一种特殊的树结构,每个节点最多有两个子节点。
  16. 二叉搜索树(Binary Search Tree):

    • 一种特殊的二叉树,左子树的节点值小于根节点的值,右子树的节点值大于根节点的值。
  17. 平衡二叉搜索树(Balanced Binary Search Tree):

    • 一种高效的二叉搜索树,保持左右子树的高度差不超过一个常数。
  18. B树(B-tree):

    • 一种多路搜索树,用于大规模数据存储和数据库索引等场景。

项目中常用的算法有哪些

  1. 排序算法:

    • 冒泡排序(Bubble Sort)
    • 选择排序(Selection Sort)
    • 插入排序(Insertion Sort)
    • 归并排序(Merge Sort)
    • 快速排序(Quick Sort)
    • 堆排序(Heap Sort)
    • 计数排序(Counting Sort)
    • 桶排序(Bucket Sort)
    • 基数排序(Radix Sort)
  2. 搜索算法:

    • 顺序搜索(Sequential Search)
    • 二分搜索(Binary Search)
    • 广度优先搜索(Breadth-First Search,BFS)
    • 深度优先搜索(Depth-First Search,DFS)
  3. 图算法:

    • 最短路径算法(Dijkstra、Floyd-Warshall、Bellman-Ford)
    • 最小生成树算法(Prim、Kruskal)
    • 拓扑排序(Topological Sort)
    • 最大流算法(Ford-Fulkerson、Edmonds-Karp、Dinic)
    • 最小费用最大流算法(Min-Cost Max-Flow)
  4. 字符串匹配算法:

    • 朴素字符串匹配算法(Naive String Matching)
    • KMP 算法(Knuth-Morris-Pratt)
    • Boyer-Moore 算法
  5. 动态规划算法:

    • 斐波那契数列问题
    • 背包问题
    • 最长公共子序列问题
    • 最长递增子序列问题
  6. 贪心算法:

    • 分数背包问题
    • 活动选择问题
    • 最小生成树问题
  7. 回溯算法:

    • 八皇后问题
    • 0-1 背包问题
    • 图的着色问题
  8. 位运算算法:

    • 位运算技巧(位与、位或、位异或、位取反、左移、右移)
    • 位图(Bitmap)算法
    • 布隆过滤器(Bloom Filter)
  9. 数论算法:

    • 素数判定
    • 欧几里得算法(求最大公约数)
    • 扩展欧几里得算法(求模逆元)

项目中用到了哪些插件

什么是I/O操作,怎么理解

webpack是什么,解决了什么问题

Webpack 是一个现代的静态模块打包工具,它的出现解决了前端开发中模块化开发、资源打包、代码优化等方面的问题。以下是使用 Webpack 的主要原因和它解决的问题:

  1. 模块化开发:

    • 在传统的前端开发中,代码通常采用全局变量和函数的方式组织,难以管理和维护。Webpack 支持使用 ES6 模块化语法或 CommonJS、AMD 等模块化方案,将代码划分为模块,方便代码组织和管理。
  2. 资源打包和管理:

    • 在 Webpack 中,可以通过配置文件指定入口文件,Webpack 会自动分析代码中的依赖关系,将各个模块打包成一个或多个文件,包括 JavaScript、CSS、图片等资源,提高了资源的加载效率和管理能力。
  3. 代码优化和压缩:

    • Webpack 提供了丰富的插件和工具,可以对代码进行优化和压缩,包括去除注释、空白字符、无用代码、合并重复代码等操作,提高了页面加载速度和性能。
  4. 开发环境和生产环境的构建:

    • Webpack 支持通过配置文件区分开发环境和生产环境的打包配置,可以根据不同环境进行代码分割、优化和压缩,提高了开发效率和产品质量。
  5. 热模块替换(HMR):

    • Webpack 提供了热模块替换功能,可以在应用运行过程中实时替换、更新模块,无需刷新页面,提高了开发效率和体验。
  6. 构建工具生态系统:

    • Webpack 作为一个灵活的构建工具,拥有丰富的插件和 loader 生态系统,可以实现各种功能和扩展,满足不同项目的需求。

React中useState都发生了什么过程

React的props和useState有什么区别