【2023】初中级前端面试笔记

浏览器

  1. 问:浏览器从标签页输入一个url,到页面渲染发生哪些
    答:浏览器打开一个标签页时会做一个无限循环,从消息队列转移到任务队列执行,浏览器监听到地址栏输入的url,会分配一个渲染进程进行页面渲染准备,同时浏览器进程会调用网络进程解析url加载资源,资源加载完,渲染进程开始解析HTML字符串渲染成像素视图。
    (1)解析url
    先本地查缓存(expires、cache-control、max-age)→获取协议、主机,端口,路径生成网络请求→DNS域名解析获取ip地址(向下递归查询,根域名到顶级域名到二级域名)→建立TCP连接三握四挥
    (2)解析html
    先解析html字符串生成可操作的dom树和样式树(parse)→经过样式计算生成布局树(style)→布局生成几何信息尺寸定位(layout)→分层堆叠上下文有关属性(layers)→生成绘制指令列表分配合成进程(paint)→每个图层分块优先展示可视窗口附件(tiles)→光栅化给块附上颜色变成位图(raster)→画生成指令quad信息动画和变形(draw)→GPU进程成像(show)

①渲染主线程:parse→style→layout→layers→paint
②合成线程:tiles→raster→draw
③GPU进程:show

①回流reflow:修改几何信息重新计算layout树,从渲染第二步开始更新,必然触发重绘;
②重绘repaint:修改颜色可见样式,从渲染第五步开始更新;
③变形transform效率高原因:在合成线程draw不影响渲染主线程,会促进GPU硬件显卡加速;

  1. 问:说说事件循环Event Loop
    答:浏览器和nodejs都有事件循环,js引擎本身不实现事件循环机制,这是由它的宿主实现的,两者实现有差别,以下是浏览器事件循环。

nodejs增多了任务类型和任务阶段,每个阶段都有单独任务队列,计时器和promise不在同一阶段,这里会和浏览器有些差异

(1)浏览器是多进程多线程,js只是其渲染进程中的一个单线程。浏览器中还有计时器线程,http请求线程和事件触发线程等。

比如,在js中发送ajax请求,请求任务是浏览器请求线程处理,结果由回调函数返回给js,即浏览器才是真正执行发送请求这个任务的角色,而 JS 只是负责执行最后的回调处理,所以异步不是js自身,是浏览器提供的。

(2)NodeJS 和浏览器的设计都是基于事件驱动的,包含事件触发、任务选择和任务执行。而事件循环其实就是在事件驱动模式中来管理和执行事件的一套流程。
(3)主线程有两个队列:任务队列(又叫执行栈)和消息队列,同步任务放在执行栈按顺序依次执行,遇到异步由其他线程执行把结果通过回调函数放到消息队列里排队,等执行栈里任务都执行完才会从消息队列里拿任务执行。
(4)事件循环就是不断检测任务队列是否有新任务产生。任务无优先,消息有优先,根据不同任务类型不同线程处理,微队列(最优)>交互队列(事件处理优高)>延时队列(计时器优中)
在这里插入图片描述
3. 问:js中计时器能做到精确计时吗,为什么
答:不能,因为计时器不是原子钟是个函数,计时器属于异步,结果会进入消息队列,而消息队列有优先级:微队列>交互队列(事件处理)>延时队列(计时器),结果会有少量偏差。

添加微队列方法:
nextTick,promise的then函数,MutationObserver
注:$nextTick执行在promise的then函数之前

  1. 问:js会阻塞页面渲染吗,为什么
    答:会,因为在使用同步方式引入js时,浏览器是按从上到下顺序加载,如果js里有很长计时器或者代码出错,也可能更改dom操作都会阻塞渲染的。

  2. 问:异步加载资源方式
    答:(1)script中defer,async和type=“module”
    defer所有渲染结束才执行,按defer声明顺序加载;
    async下载完中断渲染,执行该js文件,之后再继续渲染,无法保证执行顺序;
    type=“module”作用等同于defer;
    (2)图片懒加载
    操作dom动态添加src

var myImage = (function () {
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrc: function (src) {
            imgNode.src = src
        }
    }
})();

var proxyImage = (function () {
    var img = new Image()
    img.onload = function () {
        myImage.setSrc(img.src) //当img.src加载完成执行这个操作
    }
    return {
        setSrc: function (src) {
            img.src = src
            myImage.setSrc('/loading.gif') //预加载loading图片
        }
    }
})();
proxyImage.setSrc('/real.png');

(3)预加载link的rel
preload ,as可设置资源类型,as值不同加载优先级不同;
prefetch,其他页面需要的资源,无需指定as;
subresource,指定当前页面最高优先资源加载,无需指定as;
prerender,提前加载指定页面的所有资源,无需指定as,消耗高谨慎使用;

  1. 问:浏览器存储方式
    答:cookie,sessionstorage,localstorage,从存储大小,生命周期,场景角度说(1)cookie4kb,其他5mb;
    (2)cookie默认关闭浏览器会失效也可以通过设置Expires 和 Max-Age指定过期时间,sessionstorage当前会话结束后就会被清除,localstorage不会过期需要手动删除;
    (3)cookie用于存用户信息,设置domain,path保留二级域名登陆状态,sessionstorage记录敏感用户或页面刷新要保留临时数据如分页检索信息等,localstorage存放网络请求的不经常改的数据避免多次请求;

  2. 问:什么是cors跨域,解决办法
    答:是浏览器一种安全策略,一个请求url由协议,主机,端口,地址组成,除了地址其他相同为同源,否则跨域(Cross-Origin Resource Sharing,跨域资源共享
    (1)前端本地代理proxy
    (2)静态服务器nginx反向代理
    (3)添加响应头跨域权限Access-Control-Allow-Origin

  3. 问:浏览器缓存有哪些,如何设置
    答:常见的是内存缓存memory cache和硬盘缓存disk cache,从特点,场景,设置区别
    (1)memory cache,特点以字节形式存,读写快,标签页关闭内存释放,浏览器最先尝试的缓存,内存有限所以常存体积不大的css,js,base64图片,浏览器url历史记录。
    (2)disk cache,特点以文件形式存,被序列化加载前需要解析,读写较慢,常用来缓存网页图像、视频、静态资源等。

从缓存位置分为:
①service worker浏览器背后独立线程,要求https通过请求拦截判断是否缓存,常实现离线缓存、消息推送和网络代理等功能;
②memory cache浏览器内存中的缓存,随标签页关闭进程释放而释放;
③disk cache存储在硬盘中的缓存,容量存时间更大,根据http的协议头决定;
④push cache存在会话session中,http2的内容,缓存时间短;

(3)Disk Cache有两种:强缓存和协商缓存,都是通过http协议头来实现,强缓存优先级更高。

  • 强缓存设置响应头expires(http1.0)和cache-control(http1.1),判断是否缓存依据来自是否超出时间点或某个时间段,而不关心服务端文件是否更新,不能保证当前文件为最新,一些线上网站资源cdn可以缓存就是设置了cache-control。当强缓失效后,进行协商缓存。cache-control优先级大于expires。
  • 协商缓存依赖于服务端与浏览器之间的通信,根据文件修改时间决定是否缓存,有两个组合①服务端响应头缓存标识last-modified,请求头缓存标识if-modified-since,②服务端响应头etag,请求头if-None-Match,服务端对比两个值,未修改状态码304,否则更新状态码200。etag优先级大于last-modified。

Last-Modified有些弊端:
①我们编辑了文件,但文件的内容没有改变。服务端并不清楚我们是否真正改变了文件,它仍然通过最后编辑时间进行判断,会再次发送请求。
②它是只能秒计时,修改文件小于1s无法察觉。
Etag的弊端:Etag 的生成过程需要服务器额外付出开销,会影响服务端的性能。

补充: HTML 设置 manifest 属性所使用的应用程序缓存是一种利用硬盘缓存来存储和提供离线访问功能的机制

  1. 问:history路由和hash路由
    答:(1)history 路由,提供了对history栈中内容的操作,刷新会请求服务器,服务器允许访问,通过popstate监听变化;
    (2)hash 路由,为解决单页面路由跳转问题,无刷新跳转/不触发页面渲染,服务端获取不到hash值,通过hashchange监听变化,不利于seo;
window.history.pushState(state, title, url) 
window.history.replaceState(state, title, url)
// 与 pushState 基本相同,但她是修改当前历史记录,而 pushState 是创建新的历史记录
window.addEventListener("popstate", function() {
    // 监听浏览器前进后退事件,pushState 与 replaceState 方法不会触发              
});
window.addEventListener('hashchange', function(){ 
    // 监听hash变化,点击浏览器的前进后退会触发
})
  1. 问:不同tab页之间通信方法
    答:(1)点对点window.postMessage
    (2)Service Worker服务端缓存
    (3)cookie,localstorage利用域名限制
    (4)轮训通信: IndexedDB
    (5)非同源依靠 iframe 为媒介

htmlcss

  1. 问:flex:1,flex:auto表示什么,区别
    答:flex是项目属性,是flex-grow,flex-shrink,flex-basis的缩写,默认flex:0,1,auto表示根据空间大小不可放大可缩小项优先采用本身大小;flex:1等同flex:1,1,0%根据空间大小可放大缩小开始项目0%,多个项目flex:1不管内容多少都会平分空间;flex:auto等同flex:1,1,auto根据空间大小可放大缩小开始原本大小,多个项目flex:auto根据自身内容决定大小可能不平均。区别主要是在于 flex-basis。

  2. 问:BFC理解和应用
    答:block formatting context格式化上下文,作用于块盒,独立渲染区域,内外元素定位不受影响,常用来解决外边距重叠,清除浮动,三栏布局等。创建方法:根元素,行内块,绝对固定定位,float,overflow的auto,scroll,hidden。

  3. 问:meta的用法知道哪些?
    答:提供网页的元数据,如字符编码,视口设置,搜索引擎优化seo。常用
    (1)<meta charset="UTF-8">
    (2)name是viewport,content是设备显示区域和缩放比例
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    (3)name是keywords,方便搜索引擎查到网页
    <meta name="keywords" content="关键词1, 关键词2, ...">
    (4)name是description,简短描述吸引用户增加点击查看
    <meta name="description" content="网页描述">
    (5)name是robots,指示搜索引擎机器人如何查询页面,"index"表示允许索引网页内容,"follow"表示允许跟踪网页中的链接。
    <meta name="robots" content="index, follow">

  4. 问:rem,em,%,ex如何使用
    答:都是相对单位,rem相对根元素,给html设置font-size=12px,1rem就是12px;em不固定,em会被继承,相对于当前对象,如果没设置就会继承浏览器默认16px,最小12px,常给body设置font-size=62.5%,16px*62.5%=10px,这就使1em值变为10px。100%相对包含块(父元素或祖先元素),若包含块没指定宽度表示自适应内容。ex相对当前元素的x字符字体高度计算,常用于垂直尺寸计算。

  5. 问 从css角度优化性能方法
    答:(1)样式预加载
    <link rel='preload' href=''>
    如:link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
    (2)响应样式,配合@media避免没必要重复样式
    (3)促进gpu加速

javascript

  1. 【编程】判断两个对象内容是否相等
function equal(val1, val2) {   
    // 代码实现
    /*
     思路:
     ①分两种情况引用类型和非引用类型
     ②非引用用===
     ③引用对象层数和顺序无法确定需要考虑递归遍历
    */
    var t1=val1 instanceof Object;
    var t2=val2 instanceof Object;

    if(!t1 && !t2){
        return val1===val2;
    }

    if(Object.keys(val1).length !== Object.keys(val2).length){
        return false
    }
    
    for(var i in val1){
        var a= val1[i] instanceof Object; 
        var b= val2[i] instanceof Object; 
        if(a && b){
            equal(val1[i],val2[i])
        }else if(val1[i]!=val2[i]){
            return false;
        }
    }
    return true;
}
//console.log(equal({a: 1, b: {c: 1}},{a: 2, b: {c: 1}}));
//     输入:const value1 = {a: 1, b: {c: 1}} ; const value2 = {a: 1, b: {c: 1}}
//     console.log(equal(value1, value2))
//     输出: true 
  • 数据类型判断方法
    (1)typeof,typeof(null)typeof([])typeof({})都返回object,typeof(NaN)返回number,判断是否为null可用val==null

NaN 是特殊的数值,表示不是一个数字,而 number 是数字类型。
NaN 与任何值(包括自身)都不相等,而 number 类型的值可以进行比较操作。
NaN 是一个全局对象的属性,number 是一种数据类型。

console.log(typeof(NaN));//number
console.log(Number.isNaN(undefined));//false,先判断是否为number,再判断是否为NaN
console.log(Number.isNaN(NaN));//true
console.log(isNaN(undefined));//true
console.log(isNaN(NaN));//true

(2)Array.isArray([])返回true
(3)Object.prototype.toString.call([])返回[object Array]
(4)[] instanceof Array返回true,该方法不能判断基本数据类型,用于判断引用数据类型

  • 判断相等的方法及区别
    =====,Object.is()
    (1)==会把两边转换成相同类型
    (2)===对比引用地址,类型和值,与Object.is()区别主要在于0和NaN的处理,
    =====会将-0和+0视为相等,NaN不相等。
console.log(-0==+0);//true
console.log(NaN===NaN);//false
console.log(Object.is(-0,+0));//false
console.log(Object.is(NaN,NaN));//true
console.log(Object.is(NaN,0/0));//true
console.log(Object.is(NaN,Number.NaN));//true
  1. 问:谈谈Symbol和BigInt
    答:Symbol具有唯一性和私有性,可以定义对象的属性,常量,某类私有属性和方法。BigInt弥补了Number数值范围的限制,实现更多安全的计算,但是不能直接使用Math和Number一起计算,创建BigInt,只需要在数字末尾追加n即可,也可以通过BigInt()。

补充一:不能通过Object.keys()或者是for…in又或者Object.getOwnPropertyNames的方式来枚举,使用Object.getOwnPropertySymbols(obj) 和Reflect.ownKeys(obj)可查看
补充二:0.1+0.2≠0.3,电脑会把十进制转成二进制,二进制无法精确表示浮点

  1. 问:遇到过内存泄漏吗,哪些垃圾回收方法
    答:内存泄漏场景有①意外全局变量无法回收;②定时器未正确关闭;③事件监听未正确销毁;④闭包引用的变量未被释放;⑤dom引用未完全删除;
    垃圾回收原理未被引用的会被自动释放或手动赋值undefined,常用有①标记清除;②引用计数;③立即执行函数;④清除计时器;

javascript的垃圾回收原理:
(1)、在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
(2)、如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

  1. 【编程】防抖节流如何实现
    防抖节流区别(触发时刻不同)和使用场景
    (1)防抖:延迟执行,在等待时间内执行时间,则重置等待时间;
    延迟触发,等待事件触发完才执行;
    适用输入框搜索或验证、窗口大小调整
    (2)节流:无论事件频率如何,都会按一定时间间隔执行处理
    适用于滚动事件、按钮点击事件、输入框实时搜索、
// 防抖:某段时间执行一次
function debounce(fn, delay) {
    let timer
    return function (...args) {
      if (timer) {
        clearTimeout(timer)
      }
      timer = setTimeout(() => {
        fn.apply(this, args)
      }, delay)
    }
  }

  // 测试
  function task() {
    console.log('run task')
  }
  const debounceTask = debounce(task, 1000)
  window.addEventListener('scroll', debounceTask)
// 节流:间隔时间执行
function throttle(fn, delay) {
    let last = 0 // 上次触发时间
    return function (...args) {
      const now = Date.now()
      if (now - last > delay) {
        last = now
        fn.apply(this, args)
      }
    }
  }

  // 测试
  function task() {
    console.log('run task')
  }
  const throttleTask = throttle(task, 1000)
  window.addEventListener('scroll', throttleTask)

  1. 问:谈谈promise(MDN)实现
    答:Promise使用(避免异步编程中回调嵌套过深/回调地狱)
    精髓在于:Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态(三种状态)、传递方式来使得回调函数及时调用,它比传递callback函数要简单、灵活的多。
    (1)链式调用
    Promise then连用时,上一个then的返回值是下一个then的输入。通常用return,否则下一个then的参数为空。
    (2)静态api
    then,all,race
    race可用于判断接口请求时间是否超出指定时间
    用法小结

  2. 问:谈谈作用域
    答:作用域有助于避免变量冲突和命名空间污染
    (1)全局作用域window:程序任何地方可以访问;
    (2)var函数作用域:只能在函数内访问;
    (3)letconst块作用域:只能在块内访问,用{}区分块;

补充立即执行函数优点:
①首次执行,②隔离作用域减少命名冲突,③保护变量减少全局变量污染

  1. 问:闭包优缺点
    答:闭包是在函数内部创建函数,提供外部访问函数内变量方法
    优点:
    (1)隔离作用域,保护变量/变量私有化;
    (2)延长变量生命周期,闭包里访问的函数变量无法释放;
    (3)实现数据封装,作为回调对函数内私有变量进行访问和修改;
    缺点:内存泄漏
    闭包和回调闭包面试打印例子内存泄漏剖析

  2. 问:关于箭头函数和call,apply,bind
    答:箭头函数匿名函数,无构造函数没argument常用函数表达式,this指向永远指向上一级,call,apply,bind无法改变箭头函数中this。
    (1)func.call(obj, arg1, arg2, ...),obj是func的this对象,一个一个添加参数改变this指向;
    (2)func.apply(obj, [arg1, arg2, ...]),通过数组添加参数;
    (3)var boundFunc = func.bind(obj, arg1, arg2, ...),返回一个新函数里面包含新添加的参数;

  3. 【ts】问:type,interface,enum常见关键词作用
    答: (1)type:定义类型,可以是对象类型,任意类型(状态类、组件类),泛型,继承使用extends和&,同作用域不允许多个同名。

type Person={
	name:string;
}
type status="loading"|"success"|"error";//定义状态,联合类型
type point=[number,number]//type定义元组类型
type result<T>={//定义泛型类型别名
	success:true;
	value:T;
}|{
	success:false;
	error:string;
}

(2)interface:定义类型,可以是对象类型extends继承,类的契约implement继承,同名会自动合并。

interface Person{
	name:string;
}
interface Amy extends Person{
	work:()=>void;
}

(3)enum:具名值的集合,可以更清晰地表示状态、选项或变量的可能取值,被编译成一个对象并使用立即执行函数进行对象赋值

枚举成员默认值会被赋值为从 0 开始递增的索引数字,同时也会对枚举值到枚举名进行反向映射;
还有number类型枚举值会递增;

  1. 【ts】问:类型断言和泛型概念
    答:(1)类型断言:手动指定变量类型,有两种方式尖括号语法<string>myVariable,as语法myVariable as string
    (2)泛型:指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。使用泛型 可以复用类型并且让类型更加灵活。泛型通过使用占位符类型参数来实现,常见的类型参数名称是 T(Type),换句话说就是类型参数化。
/*
常用泛型函数
理解:<T, U>为参数类型,()里是传的参数这里是一个数组,冒号后[U,T]表示函数返回对象类型
*/
function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}
console.log(swap<number, string>([7, 'seven']));

  1. 问:立即执行函数优点
    答:(1)首次执行
    (2)隔离作用域:独立块,减少命名冲突
    (2)模块化开发
    (3)有些闭包,减少全局变量污染

  2. 问:浅拷贝拷贝不了的问题,解析序列化解析不出来的情况
    答:(1)被拷贝的对象有多层结构,引用对象变化时,导致浅拷贝拿到的数据也是变化的
    (2)某些对象类型可能不支持浅拷贝未实现__copy__,不支持序列化未实现__reduce__

  3. 问:for in和for of 区别
    答:前者返回一个键名,后者返回一个键值
    for…in会遍历对象的整个原型链, 性能非常差不推荐使用,而for…of只遍历当前对象不会遍历原型链;对于数组的遍历,for…in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of只返回数组的下标对应的属性值;
    总结:for…in循环主要是为了遍历对象而生,不适用遍历数组; for…of循环可以用来遍历数组、类数组对象、字符串、Set、Map以及Generator对象

14、排序
(1)冒泡排序,时间复杂度 O(n^2),空间复杂度 O(n)。i和i+1比较

var arr = [25,13,33,8,23,32];
Array.prototype.sort = function(callback){
     var arr = this;
     var i = 0;//i在这里定义与在for循环的括号内(for(var i = 0; i < ...))定义是一样的
     for(; i < arr.length-1; i++){
          var j = i + 1;
          for(; j < arr.length;j++){
               if(callback(arr[i],arr[j])){
                    var temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
               }
          }    
     }
return arr;
};
//a-b>0表示数组从小到大排序
arr.sort(function(a,b){
     return a - b > 0;
});
document.write(arr.join(",") + "<br />");//8,13,23,25,32,33
//b-a>0表示数组从大到小排序
arr.sort(function(a,b){
     return b - a > 0;
});
document.write(arr.join(","));//33,32,25,23,13,8

(2)快速排序,时间复杂度 O(nlogn),空间复杂度 O(n)。
x基准左left右right指针

while(left <= right) {
        while(left <= right && arr[left] >= x) left++;
        while(left <= right && arr[right] <= x) right--;

        if(left < right) {
            let t = arr[left];
            arr[left] = arr[right];
            arr[right] = t;
        }
	}

框架

  1. 问:谈谈虚拟dom的理解
    答:它是一个在内存中以 js对象形式表示的轻量级的 DOM 抽象,通过对比前后两个虚拟 DOM 的差异,最小化实际 DOM 更新的操作,从而提高性能。
    初始阶段:会产生真dom和js虚拟dom;
    更新阶段:产生最新虚拟dom,前后对比找出最优更新,这个过程又叫虚拟dom的 diff;
    渲染阶段:用最小的操作来更新真dom,这个过程又叫虚拟dom的 patch;

补充react18,vue2,vue3的diff算法区别:

都基于以下3个原则:①只比较同级节点;②新老节点类型不同,先删除再新建;③用key做新老节点的标识符(都采用map数据结构);

(1)react:原本reactelement只有children,在中断恢复时无法找寻兄父,而fiber节点可访问父子兄节点渲染被打断也可以恢复查找未处理的节点,因此react需先生成reactelement(react.createElement新),再生成fiber树(旧),新旧对比再更新真实dom,这与vue大不同。采用深度优先算法先比较key,key和type都相等复用节点,反之直接删除,移动比较少所以react的differ会进行两轮遍历。
(2)vue2:双指针(双端),每次遍历进行最多4次双端比较(新前vs旧前,新后vs旧后,新后vs旧前,新前vs旧后),比较过程中对节点直接进行更新增删移动,会导致很多无效重新渲染。
(3)vue3:双指针,先比较首尾节点,根据新节点的位置索引,给老节点做标记,采用最长递增序列算法计算不需移动节点,从后往前创建或复用节点。

思考:key和name关键字作用
key:用于优化列表渲染,使框架快速识别,进行操作更新
name:用于命名函数、对象属性、表单字段等,具体作用取决于上下文

  1. 问:谈谈Vue 3 Proxy 和 Reflect
    答:双向绑定原理通过数据劫持监听数据(对象)变化执行事件处理(函数),vue3中就使用这两个对象替代vue2中的Object.defineProperty。Proxy对象允许拦截并自定义对对象的操作,像get,set,delete等,优点是对整个对象进行代理,可以监听新增属性和删除属性。Reflect是js内置对象,提供操作对象的方法,像get,set,delete等。Reflect对象被用于在vue3 Proxy中的代理处理函数中,提供用于操作目标对象的默认行为。

  2. 问:vite特点,常用构建配置
    答:优点
    (1)原理:利用浏览器值支持ESM(ESModule/ES6的import),按需加载(遇到import浏就会发送http),快速冷启动,采用esbuild和nobundle机制(预编译和不打包)。

对比webpack处理过程:
①webpack:先解析依赖→loader打包构建→启动服务→监听依赖文件变化→重新打包
②vite:先启动服务器分模块→响应浏览器动态加载模块→监听依赖文件变化→只更新变更模块

(2)基于Rollup打包:生产下由esbuild对css和代码分割模块并使用rollup进行打包。
(3)使用esbuild预编译&预构建:
①支持非ESM格式依赖包,需要将commonJS文件提前转化成ESM,缓存到node_modules/.vite
②减少模块触发浏览器并发请求限制,因为有的依赖文件太大会被转换成多个模块,为了提高加载性能
③esbuild快,使用GO语言,可直接转化成机器语言,多线程充分利用CPU资源
(4)更快热更新HRM:监听文件更新,只对变更模块重新加载,同时利用http缓存加速页面加载,源码使用协缓,依赖使用强缓
缺点:unbundle机制大量http请求会导致首屏和懒加载的性能下降
(感觉就是把webpack打包时间转移到服务器响应浏览器请求时间了)

  1. 问:优化首屏加载的一些思路方法
    答:(1)减少文件大小:webp,base64,雪碧图,cdn资源
    (2)异步预加载和懒加载
    (3)合理使用缓存:cdn缓存,浏览器缓存
    (4)服务器端渲染(ssr):用Vite配合框架的SSR功能,将HTML的生成和一部分数据的预获取放在服务器端进行处理
    (5)减少http请求数量,使用http2合并请求

  2. 问:选项式和组合式区别
    答:(1)选项式:声明选项组件,有data,methods,生命周期都暴露在函数里,可以通过this访问到,vue2的书写规范;
    (2)组合式(composition):由一系列API合集,有响应式API(ref基本数据类型和对象类型,reactive对象类型),搭配setup(标识告诉vue做编译处理),vue3新增的一种规范;
    总结:选项式是以组件实例为中心,构造函数即this为核心;组合式的核心是在函数作用域里定义响应式,对于组件通信更自由(只要引入对于响应式函数就可以),两种也可以混合开发。
    组合式优点:①解决选项式mixins缺点,②纯函数不会自动引入生命周期代码压缩友好

组合基于函数但不是函数式编程
函数式编程:类似react,有参数,无状态,可以数组的map,reduce渲染

  1. 问:watch、watchEffect、computed区别

  2. 问:自定义组件如何定义v-model
    答:绑定change事件,通过emit去更新通知父组件;v-model里的data从props中获取;使model中even事件与emit事件保持一致

  3. 问:pinia实现原理
    答:一个独立组件/单元由状态,视图和组件构成。多个视图可能都依赖于同一份状态。一个state是一个reactive响应式对象。
    与 Vuex 相比,Pinia 不仅提供了一个更简单的 API,也提供了符合组合式 API 风格的 API,最重要的是,搭配 TypeScript 一起使用时有非常可靠的类型推断支持。mutation 已被弃用,它有三个概念,state、getter 和 action(同步和异步),支持热更新。

  4. 问:迭代组件,停止条件
    答:树结构dom,渲染相同样式可使用迭代组件,停止条件就是判断它是否有child

网络

  1. 问:你用过哪些网络通信方式
    答:fetch、ajax 和 beacon
    (1)fetch:基于 Promise,提供了api,可以发送 GET、POST、PUT 等不同类型的请求,并支持处理响应数据,不依赖其他库;
    (2)ajax:使用 XMLHttpRequest 对象来发送和接收数据,允许在网页上进行异步数据交换,常使用vue和jquery库;
    (3)beacon:window的一种用于发送异步请求的api,适用于发送收集数据、统计或日志等无需等待响应的请求;

beacon API 提供了 navigator.sendBeacon() 方法,可以在后台发送数据,以确保即使在页面关闭或卸载时,数据也能成功发送到服务器

  1. 问:网络请求方式methods有哪些,区别
    答:(1)post:常用于表单提交,数据放到请求体data里;
    (2)get:常用于请求查询数据,查询条件拼接在url后面;
    (3)put:常用于更新数据,url包含更新id,请求体包含更新内容;
    (4)options:用于请求预检,不请求数据只是询问服务器是否支持该请求
    (5)delete:删除资源

  2. 问:列举一些请求头
    答:(1)host:主机地址
    (2)user-agent:用户环境信息(设备,浏览器,操作系统等)
    (3)accept:客户端允许接收的数据类型,如arrarbuffer,json,string
    (4)origin:来源
    (5)referer:上一个页面
    (6)conection:keep-alive和close

  3. 问:遇到的状态码
    答:200成功,
    3:重定向
    304协缓判断文件未更新,
    4:客户端错误
    400错误请求,401未授权,404路径找不到
    5:服务端错误
    500服务器错误,503服务器不可用

  4. 问:在网络通信中,数据过大可能会导致粘包(多个包被合并为一个包)或拆包(一个包被拆分为多个包)的问题
    答:(1)固定长度分包(Fixed-Length Framing):
    发送端在发送数据之前,先将数据按照固定的长度进行划分,然后发送。接收端根据指定的长度来截取每个数据包。
    优点: 简单易实现。
    缺点: 当数据长度不是固定的时候可能会浪费空间,也可能无法处理变长数据。
    (2) 分隔符分包(Delimiter-Based Framing):
    每个数据包之间使用特定的分隔符进行分隔。接收端根据分隔符来判断每个数据包的边界。
    优点: 适用于变长数据。
    缺点: 如果数据中包含了分隔符,可能需要进行转义处理。
    (3)长度字段标识分包(Length Field Based Framing):
    在每个数据包的开头加上一个表示数据长度的字段。接收端首先读取长度字段,然后根据该长度来读取实际数据。
    优点: 适用于变长数据,同时避免了分隔符可能引起的问题。
    缺点: 需要在数据包中增加长度字段,稍微增加了复杂性。
    (4)应用层协议设计:
    设计一个应用层协议,明确定义数据包的格式。协议中包含数据的起始标志、长度字段等信息,以确保接收端能够正确解析数据包。
    优点: 可以根据具体需求进行定制化,适用性强。
    缺点: 需要额外的协议设计和解析工作。
    (5)使用消息边界(Message Boundary):
    确保每个消息都有一个明确的结束标志,这样接收端就能正确地辨别每个消息的边界。
    优点: 简单易实现。
    缺点: 需要约定好消息的结束标志,可能会受到特殊字符的影响。
    在实际应用中,一些框架和库提供了对分包和粘包问题的解决方案,可以方便地集成到项目中,如Netty、Protocol Buffers等。

  5. 问:HTTP/1,HTTP/2,HTTP/3
    答:(1)连接传输
    http1,采用字节明文传输,连接慢,需要多次往返通信;
    http2,采用二进制分帧传输,j加密更安全;
    http3,基于UDP的QUIC协议,减少了连接延迟,引入了流(streaming)的概念,允许更高效的数据传输。
    (2)多路复用
    http1顺序请求;
    http2,3多路复用并发请求;
    (3)头部压缩传输
    http1每次传完整头;
    http2,3,压缩传输的数据
    (4)http2,3服务推送资源缓存,需要开发配置

git

  1. 问:返回指定提交版本
    答:查看记录git log
    (1)git reset --hard commitId彻底删除commitID之后所做的改动,代码也一起回退回来了。 (慎重用,用前最好备份一下代码,或者用git diff 生成一个patch)
    (2)git reset --soft commitID只删除commitID之后的提交记录log,代码的改动还在。
  2. 问:合并冲突
    答:git merge 或 git rebase
  3. 问:分支命令用过哪些 branch
    答:查看分支状态git status
    查看所有分支git branch -a
    (1)git branch
    创建分支git branch branch_name
    删除分支git branch -d branch_name
    (2)git checkout
    切换分支git checkout branch_name
    从某个版本创建分支git checkout -b branch_name ef238cb373abfa73617a32d633e29399d418c628

切换到指定提交:git checkout commit_hash,用于将工作目录恢复到指定提交的状态
恢复文件:git checkout – file_path,用于将指定文件恢复到上一次提交的状态

(3)git fetch
创建/拉取分支git fetch remote_name branch_name
git fetch origin master

总结:git checkout 用于切换分支、恢复文件的状态。git branch 用于查看、创建和删除分支。git fetch 用于从远程仓库下载最新的提交和分支信息。

平时用git tag标记测试,发布,开发版本

常见问题

  1. 问:消息推送的实现
    答:(1)本地推送,满足触发条件时调取应用程序的API(适用平台: iOS、Android、Web 等)
    (2)远程推送,使用推送服务器向设备发送通知(适用平台: iOS、Android、Web 等)
    (3)websocket协议,应用和服务器建立长链接,服务器可以实时向客户端推送消息(Web、移动应用)
    (4)长轮询,客户端向服务器发送请求,服务器保持连接打开,直到有新消息时才响应(适用于不支持WebSocket的环境)
  2. 问:token失效无感刷新方法
    答:(1)使用刷新令牌(Refresh Token),除了基本token,提供一个更长久的用于刷新token
    (2)使用静默刷新,当访问令牌即将过期或失效时,前端应用可以在后台自动发起令牌刷新请求,获取新的访问令牌。

小程序

  1. 小程序消息订阅一次和长期条件
  2. 登录逻辑
    wx.getUserProfile申请用户权限会出弹框
    wx.getSetting获取当前对象申请过的权限
    wx.login获取openid
    wx.request存到服务器获取token(无感刷新)

补充:
Ahooks
数据结构概念和常见算法
数据结构
性能优化参考
js内置对象Global 和 Math的使用总结
ES6之后新特性
JS一些底层技术原理实现

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值