0


前端笔记+面试

一:JS基础部分

  1. 原型链和原型每个对象都有_proto_属性,并且指向它的原型对象每个构造函数都有它的prototype原型对象prototype原型对象里的constructor指向它的构造函数new一个构造函数会形成它的实例对象
  2. 深拷贝与浅拷贝涉及堆栈内存、引用类型的区别如何深拷贝:递归拷贝&&利用JSON函数深拷贝 JSON.parse(JSON.stringify({对象}))
  3. 堆栈内存堆内存:接受引用类型数据,引用类型数据将会保存在堆内存中,变量储存该引用类型的地址,将其指向堆内存的真实数据。栈内存:储存基本类型数据,变量声明的基本类型数据将直接储存在栈内存中
  4. 递归和闭包的区别funcName()functionfuncName(){// 函数体 }// function funcName (){} 变量会提升varFuncName=function(){}// 变量提升,但是并没赋值,会提示undefined// 1. 递归就是在函数内再次调用自身// 2. 闭包:在A函数内声明一个函数B// 3. 闭包的缺点// 由于闭包会携带包含它的函数的作用域,因此会比其他正常的函数占用更多的内存// 闭包只能取到包含任何变量的最后一个值// 3. 闭包的优点:// 模仿块级作用域,在构造函数中定义特权方法,静态私有变量,模块模式
  5. promise then catch 如何封装一个函数functionfn1(){returnnewPromise((resolve, reject)=>{setTimeout(()=>{ console.log('fn1...')resolve()},1000)})}functionfn2(){returnnewPromise((resolve, reject)=>{setTimeout(()=>{ console.log('fn2...')resolve()},1000)})}functionfn3(){returnnewPromise((resolve, reject)=>{setTimeout(()=>{ console.log('fn3...')resolve()},1000)})}functiononerror(){ console.log('error')}fn1().then(fn2).then(fn3).catch(onerror)
  6. Array.reduce()方法const arr =[1,2,3,4,5,6,7,8]const maxSum = arr.reduce((previousValue,nextValue,index,arr)=>{return Math.max(previousValue,nextValue)},0)// reduce方法第一个参数为回调函数,第二个参数为回调函数的初始值。// reduce是数组的归并方法,与forEach,map,filter,every等方法一样会进行数组的遍历,但是reduce可同时将前面数组遍历过的值产生的累计值与当前遍历项进行运算,这是前几个方法无法做到的。
  7. Array.every() | map() | filter() | forEach() | some()方法// every用于处理数组``每一项``是否满足条件,返回Boolean值// forEach类似for循环,简单遍历当前数组,不返回值// map遍历处理数组后返回一个新的数组// filter处理每一项是否符合条件,返回符合条件的项的新数组// some用于返回数组是否存在满足条件的项,返回Boolean值
  8. JS的封装原理// 暂无
  9. JS的防抖节流定义:在进行窗口的resize、scroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果。 函数防抖函数防抖(debounce):对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。// 防抖functiondebounce(fn, wait){var timeout =null;returnfunction(){if(timeout !==null)clearTimeout(timeout); timeout =setTimeout(fn, wait);}}// 处理函数functionhandle(){ console.log(Math.random());}// 滚动事件window.addEventListener('scroll',debounce(handle,1000));节流:设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(类似于技能冷却时间)。效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。// 节流函数functionthrottle(fn,delay){let valid =truereturnfunction(){if(!valid){//休息时间 暂不接客returnfalse}// 工作时间,执行函数并且在间隔期内把状态位设为无效 valid =falsesetTimeout(()=>{fn() valid =true;}, delay)}}/* 请注意,节流函数并不止上面这种实现方案, 例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。 也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样 */// 以下照旧functionshowTop(){var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;  console.log('滚动条位置:'+ scrollTop);}
  10. 普通函数和箭头函数的区别
```js
1. 箭头函数的定义:相当于匿名函数,两种格式:一为只包含表达式,没有{函数体},二为包含多语句,不能省略{函数体}和return;
2. 箭头函数是匿名函数,不能作为构造函数,不能使用new;
3. 箭头函数不绑定arguments,而用rest参数...取而代之;
4. 箭头函数不绑定this值,只捕获上下文的this;
5. 箭头函数通过apply和call方法调用一个函数时,只传入了一个参数,不影响this;
6. 箭头函数没有原型属性。
7. 箭头函数不能作为Generator函数,不能使用yield关键字

1. ###### JS事件循环(Event Loop)
简化版:JS先执行主线程的同步任务,然后再到事件队列中拉出宏任务和微任务放入主线程调用。

1. 基础
   单线程:JS本身是单线程,这是因为JS本身是为浏览器交互而生的语言,单线程避免了多线程任务对同一DOM的操作造成的混乱。
   伪多线程:虽然现在有Web Worker用来模拟多线程,但是本质还是受控于主线程下的多子线程,且子线程不允许操作DOM。
   任务类型:在主线程上的都是同步任务,异步任务会放在子线程运行,运行完后会加入事件队列。
   执行栈:所有等待在主线程上被执行的同步任务,形成了执行栈。
   宏任务:setTimeout, setInterval, 整体代码script。
   微任务:Promise.then, Promise.nextTick,在事件队列中,微任务优先级高于宏任务,事件队列中的宏任务会放在下一次循环中执行。
   执行顺序:宏任务>>宏任务执行结束>>有微任务吗?执行微任务:下一次事件循环 >>> 开始新的宏任务
2. 运行逻辑
   进程监控:JS有一个监控进程(monitoring progress),用来监控执行栈是否有同步任务在队列等待执行,如果执行栈中没有任务,将会在事件队列中调用等待被调用的函数进入执行栈。
   事件循环:根据上述的进程,当执行栈和事件队列的事件执行完毕后,会再次从头开始,形成循环,为此叫做事件循环。
3. 注意
   await属于同步任务,await以下的代码块属于异步任务,会放在时间队列中等待执行。
   new Promise会立刻进入主线程执行,then函数分发到微任务事件队列。

1. ###### Promise
new Promise((resolve,reject)=>{
if (true){
    const res = {}
    return resolve(res) // 成功状态
} else {
    const error = "error"
    reject(error) // 失败状态
}
})

### 二:浏览器部分

1. ###### 浏览器从打开URL到数据渲染的原理```1. DNS 解析2. HTTP三次握手 -> TCP/IP连接   第一次握手就是客户端给服务器端发送一个报文,第二次就是服务器收到报文之后,会应答一个报文给客户端,第三次握手就是客户端收到报文后再给服务器发送一个报文,三次握手就成功了。      三次握手(Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。3. 浏览器发送请求4. 服务器返回请求的文件 (html)5. 浏览器渲染   浏览器拿到了服务器返回的HTML页面代码,在解析和渲染这个页面时候,里面的JS/CSS、图片静态资源,他们同样    也是HTTP请求,都需要经过上面的步骤。      浏览器根据拿到的资源对页面进行渲染,最终把一个完整的页面呈现给了用户。   HTML parser:根据 HTML 构建 DOM 树。   CSS parser:根据 CSS 构建 CSSOM 树。      如果遇到 script 标签的话,会判断是否存在 async 或者 defer ,前者会并行进行下载并执行 JS,后者会先    下载文件,然后等待 HTML 解析完成后顺序执行。如果以上都没有,就会阻塞住渲染流程直到 JS 执行完毕。遇到    文件下载的会去下载文件,这里如果使用 HTTP/2 协议的话会极大的提高多图的下载效率。      Attachment:CSSOM 树和 DOM 树构建完成后会开始生成 Render 树。   Layout: 布局。确定页面元素的布局、样式等等诸多方面的东西。   GPU painting: 像素绘制页面,将内容显示在屏幕上了。```
2. ###### Cors是什么?如何解决线上跨域```**CORS**(Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输的[HTTP头]组成,这些HTTP头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。如果 后台(server) 端是自己开发的,那么修改相关代码支持跨域即可。如果不是自己开发的,那么可以自己写个后端转发该请求,用代理的方式实现。使用代理进行请求```
3. ###### 本地储存有哪些?COOKIE跨域```// 1)localStorage和sessionStorage都具有相同的操作方法// 2)localStorage长期存放,sessionStorage浏览器关闭时清空1.Cookie 在浏览器请求中 每次都会附加请求头发送给服务器;2.localStorage 保存数据会一直保存,没有过期时间;3.sessionStorage 仅当前页面有效,一旦关闭就会被释放;4.cookie为4KB,而WebStorage是5MB;```
4. ###### 如何解决页面卡顿的问题及虚拟渲染``` 1.使用懒加载和使用异步组件懒加载   方法:component: resolve=>(require(['地址']), resolve) 2.ES提出的import方法(最常用)    const HelloWorld = ()=>import('地址') 3.使用懒加载 4.虚拟渲染:   一般情况下用于表格展示,只展示给用户屏幕区域内的数据,而不是原生浏览器那样渲染所有数据,再通过屏幕滚动移动页面。 5:使用CDN分流。 6:使用HTTP缓存。```
5. ###### 前端缓存策略```http缓存:当浏览器请求服务器资源时,会先在客户端中检测是否存在资源副本,如果有,会直接提取浏览器缓存,而不是从服务器获取。又分为强制缓存,协商缓存,决定字段:Pragma, expires, cache-control。当P和C共存时,P优先级更高。强制缓存:强制缓存会在未失效的情况下,一直使用该缓存,不会再向服务器请求该资源,如果服务器的该资源发生变动,客户端则依旧不会更新。协商缓存:协商缓存会在每次请求上和服务器进行协商,当服务器资源未修改,则返回304,客户端缓存还能继续使用,如果资源更新,服务器返回200,会将新资源一同返回,客户端则会使用服务器返回的新资源。但是每次都要与服务器通信,对服务器压力很大。解决方案:优先使用强制缓存,服务器告知客户端缓存有效时间,在有效时间内直接调用强缓存,过了有效期就执行协商缓存策略。```
6. ###### web系统的优化方法```1. 减少HTTP请求: 减少图片的请求、减少脚本文件与样式表的请求2. 压缩代码 zip-webpack-plugin3. 通过异步的方式加载async1.js文件:<scriptsrc="./async1.js"async></script>4. 使用强缓存:不用请求服务器,直接使用本地的缓存。   强缓存是利用 http 响应头中的Expires或Cache-Control实现的。【重要】5. 协商缓存:浏览器发现本地有资源的副本,但是不太确定要不要使用,于是去问问服务器。   当浏览器对某个资源的请求没有命中强缓存(也就是说超出时间了),就会发一个请求到服务器,验证协商缓存是否命中。```
7. ###### 浏览器缓存加载速率是多少秒```// 暂无```
8. ###### 移动端布局如何实现响应式```flex、rem、grid.```
9. 回流和重绘```回流:指DOM树中一部分或者全部的节点发生了变化,浏览器会将这部分重新构造在DOM树中,并在浏览器重新渲染。重绘:不会影响布局的部分属性发生变化后,触发视图的更新。区别:回流一定引发重绘,但是重绘不一定引发回流,只有影响到布局的重绘才会触发回流。```

### 三:VUE部分

1. ###### v-model的实现原理```简单:v-model其实是个语法糖,它实际上是做了两步动作:1、绑定数据value2、触发输入事件input,监听input输入框里值的变化,然后改变值详细:v-model是:value="msg" @input="msg=$event.target.value"的语法糖,其中:value="msg"是绑定了数据,value就是input输入框里的值;@input="msg=$event.target.value"就是监听input输入框里值的变化,然后改变值。一句话概括就是,绑定数据并且监听数据改变```
2. ###### vue3 proxy```Proxy 是 JavaScript 2015 的一个新特性。Proxy 的代理是针对整个对象的,而不是对象的某个属性,因此不同于 Object.defineProperty 的必须遍历对象每个属性,Proxy 只需要做一层代理就可以监听同级结构下的所有属性变化,当然对于深层结构,递归还是需要进行的。此外Proxy支持代理数组的变化。```
3. ###### vue如何渲染标签内容(DOM树渲染流程)```// vue的template只是模板语法,不是真实的DOM节点{1. vue将模板编译为render函数;    2. 比对虚拟DOM,渲染至真实DOM;    3. 当组件内data发生变化时,组件和子组件引用data作为props重新调用函数,生成虚拟DOM,再次比对虚拟DOM,渲染至真实DOM。}```
4. ###### 如何解决vue工程化、代码量越多带来的本地编译速度慢的问题```1. 安装babel-plugin-dynamic-import-node插件(动态加载依赖)   在.babelrc文件夹添加一段代码就可以解决, "plugins": ["dynamic-import-node"]2. 动态路由:使用require方式代替import方式```
5. ###### vue父子组件的生命周期:父子组件加载优先级及加载的周期```组件的调用顺序都是先父后子渲染完成的顺序是先子后父组件的销毁操作是先父后子销毁完成的顺序是先子后父加载渲染过程父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount- >子mounted->父mounted子组件更新过程父beforeUpdate->子beforeUpdate->子updated->父updated父组件更新过程父 beforeUpdate -> 父 updated销毁过程父beforeDestroy->子beforeDestroy->子destroyed->父destroyed```
6. ###### 登陆前如何判断用户是否有权返回该页面(路由守卫)```// 执行时机:当点击路由时,进入该组件前调用的函数router.beforeEach(to,from,next){// to 表示当前要进入的路由// from 表示上一个路由  // next() 表示下一步要执行的业务代码, 如next('/login')// 举个栗子if(sessionStorage['username']){next();}else{next('/login');}// 或者if(to.path =='/login'|| to.path =='/register'){next();}else{alert('您还没有登录,请先登录');next('/login');}}```
7. ###### vuex中mutation和action的区别```{1.流程顺序:先触发action,action再触发mutation,    2.角色定位:mutation是理论上修改state的唯一途径,action是逻辑代码加异步请求,    3.mutation必须同步执行,action可以同步执行,但不能直接操作state}```
8. ###### VUE数据双向绑定原理(Object.difinedProperty)```{1.vue的双向绑定主要是采用数据劫持的方式,结合"发布者-订阅者"模式,通过Object.difinedProperty()方法,来劫持每个属性的getter和setter。并在数据发生变动时给订阅者发布消息,触发相应的监听回调。    2.vue的数据双向绑定是基于Object.definedProperty()方法,通过定义data属性的get和set函数来监听数据对象的变化,一旦变化,vue利用发布订阅模式,通知订阅者执行回调函数,更新DOM。}// 缺点: 通过下标方式修改数组数据或者给对象新增属性并不会触发组件的重新渲染```
9. ###### VUE所有的传参方式```1. 属性传值: 仅适用于父组件给子组件传值2. $refs: 父组件通过$refs获取子组件的数据和方法3. $parent: 子组件通过$parent获取父组件的数据和方法4. 通知传值(广播传值)   通知/广播传值我们定为只传基本数据类型,不能传方法.   VueEmit.$emit('名称','数据')传播数据   VueEmit.$on('名称',function(){})接收数据   无关系组件不能用这种方式传值5. 本地传值   一种是JS的localStorage,另一种Vuex6. 路由传值   父组件push使用this.$router.push   在子组件中获取参数的时候是this.$route.params7. provide,inject 传值```
10. ###### 模拟vue双向绑定源码```<body><div id="demo"></div><input type="text" id="inp"/><script>const obj ={};const demo = document.querySelector("#demo");const inp = document.querySelector("#inp");      Object.defineProperty(obj,"title",{get:function(){return val;},set:function(newVal){//当该属性被赋值的时候触发          inp.value = newVal;          demo.innerHTML = newVal;},});      inp.addEventListener("input",function(e){// 给obj的name属性赋值,进而触发该属性的set方法        obj.title = e.target.value;});      obj.title ="hahah";//在给obj设置name属性的时候,触发了set这个方法</script <body><div id="demo"></div><input type="text" id="inp"/><script>const obj ={};const demo = document.querySelector("#demo");const inp = document.querySelector("#inp");  Object.defineProperty(obj,"title",{get:function(){return val;},set:function(newVal){//当该属性被赋值的时候触发      inp.value = newVal;      demo.innerHTML = newVal;},});  inp.addEventListener("input",function(e){// 给obj的name属性赋值,进而触发该属性的set方法    obj.title = e.target.value;});  obj.title ="hahah";//在给obj设置name属性的时候,触发了set这个方法</script></body>```
11. ###### vue router的两种模式的区别(hash,history)```1. 形式上:hash模式的url永远带着#号,开发中会默认使用hash模式。如果需要考虑URL的规范,就需要使用history模式,因为history模式没有#号,比较符合规范。2. 功能上:第一点:部分app会屏蔽或无法识别链接中的#号,而history模式就没有#号,更便于分享和访问链接;第二点:history模式在访问二级页面的时候,当刷新的时候会出现404错误。这需要后端配合配置apache或者nginx重定向至当前路由或首页。```
12. ###### vue的生命周期```1. beforeCreate   在组件实例初始化之后,组件选项对象还未创建,el和data还未初始化,无法访问methods, data, computed等。2. created   组件实例创建完成后被调用,这时候data,methods, computed等数据和方法加载完毕并可以使用,但是el还未加载完成。3. beforeMounte   实例挂载之前被调用,已完成虚拟DOM的创建,但还未加载至页面上。4. mounted   实例加载完成被调用,此时DOM已加载完毕。此时可以进行AJAX操作(官方推荐)5. boforeUpdate   数据更新前被调用,发生在虚拟DOM重新渲染或者修改之前,可以在这个钩子里进一步修改状态,不会重新触发渲染。6. updated   数据更新导致的虚拟DOM渲染和更新完成后才被调用,但是尽量不要在此钩子中处理数据,否则可能会导致无限循环。7. boforeDestoye   组件实例销毁前被调用,此时实例依然完全可用,一般用来销毁定时器和DOM监听事件8. destroyed   组件已完全销毁,组件内数据和事件无法访问。keep-alive的周期9. actived   当前组件被激活时调用的钩子10. deactived   当前组件被停用时调用的钩子```

### 四:CSS部分

1. ###### 简单实现CSS三角形```.triangle{width: 0;height: 0;border-top: 50px solid transparent;border-bottom: 50px solid transparent;border-left: 50px solid red;}```
2. ###### flex布局```.box{display: flex;flex-direction: column | column-reverse | row | row-reverse;flex-wrap: nowrap | wrap | wrap-reverse;/* wrap-reverse 换行-第一行在下方 */flex-flow: flex-direction || flex-wrap;/* 该属性为flex-direction和flex-wrap的简写,默认row nowrap */justify-content: flex-start | flex-end | center | space-between | space-around;/* space-between 中间间隔相等,space-around 相对于between两侧会留空,项目之间的间隔比两侧间隔大一倍 */align-items: flex-start | flex-end | center | baseline | stretch;/* baseline:与项目第一行文字对齐,stretch:默认占满容器的高度 */align-content: flex-start | flex-end | center | space-between | space-around;/* 轴线的对齐方式,如果只有一根轴线则不起作用 */order: <interger>;/* flex排序,数字越小越靠前 <整数> */flex-grow: <number>;/* 自定义放大比例,默认为0 */flex-shrink: <number>;/* 自定义缩小比例,默认为1 */flex-basis: <length> | auto;/* 默认值auto, 可定义分配主轴空间前占据的空间,可设定具体PX值 */flex: none | [<flex-grow>, <flex-shrink>, <flex-basis>];/* ;两个快捷值: auto (1,1,auto)和none(0,0,auto) */align-self: auto | flex-start | flex-end | center | baseline | stretch;;/* 允许单个项目与其他项目不同的对其方式,默认auto,表示继承父元素align-items属性,如没有父元素则等同于stretch */}```
3. ###### grid布局```Flex布局是轴线布局,只能指定"项目"针对轴线的位置,可以看作是一维布局,Grid 布局则是将容器划分成“行"和“列”,产生单元格,然后指定"项目所在”的单元格,可以看作是二维布局,Grid布局远比 Flex布局强大。(不过存在兼容性问题,使用之前应看具体需求)```

## **2021-11-24补充**

#### 一:VUE3和VUE2的区别

1,Vue2和Vue3的响应式区别

Vue2的响应式原理是通过监听器(Observer)遍历每个对象的属性,再通过Object.defineProperty给每个属性转换为getter和setter,以此来监听值的访问和修改。当属性的值变化后,监听器就会通知订阅者(Watcher)进行更新。

因为Object.definedProperty的局限性,所以带来的缺点显而易见,当对象的属性增加或者删除之后,Object.definePropery无法监听到其变化。

数组响应式:通过重写数组常用的方法,结合监听器,做到劫持数组数据的变化,缺点是通过下标修改数组数据或者修改数组长度时,无法响应。

Vue3是通过Proxy(代理)实现响应式,Proxy可以直接监听对象或者数组,避免了Object.defineProperty的缺点,但是Proxy只会代理对象的第一层,Vue3通过递归再次代理下一级对象,当数据发生变化时,再通过Reflect反射回原数据进行更改。

2,Diff的区别

服务器返回的HTML文本经过浏览器解析后构建真实DOM, 然后vue会构建虚拟DOM (js对象), 如果数据更新,会先反应在虚拟DOM上 也就是一个新的js对象 然后会有diff算法(其实就是调用patch函数,进行比对)同级对比新旧两个虚拟DOM对象,然后通过订阅者(Watcher)调用patch方法对仅仅发生变化的DOM节点进行更新。

Vue3 diff的区别

上述说了vue2会对比两个新旧虚拟DOM对象,这种是全量对比,比较消耗性能,vue3在动态的DOM节点上新增了patch flag作为动态节点,只需要对比动态节点,触发更新即可。

3,hoist static 静态提升

Vue2中的DOM元素无论是否是静态DOM,在DOM更新时都会重新参与渲染,而在VUE3中,通过提升静态DOM达成复用避免重复渲染,以此提升性能。

4,cacheHandlers 事件侦听器缓存

VUE2的绑定事件是动态绑定的,每一次调用函数都会重新生成,造成不必要的性能开销,vue3中提供了cacheHanlder, 当开启后,将会生成内联函数和静态节点缓存以便下次调用。

5,更好的TS支持

由于vue2的Option Api的设计导致无法很好地和ts匹配(Option Api是简单对象,TS是面向对象和类型的语言),如果要在Vue2中使用ts,要安装依赖和装饰器,最终导致代码风格无法统一。
Vue3很好的支持了TS,在组件内可直接使用TS的类型推断,比较典型的就是composition API中的reactive,ref。

6,composition API 组合式API

更好的维护逻辑代码,可以在一个JS文件维护一个业务逻辑相关的代码,还可以使用VUE的API,不像VUE2只能固定的Option API写法且维护困难。

7,支持多个根节点,VUE3会在根节点自动创建一个虚拟的根节点。

8,自定义渲染API

可使用Customer Render Api自定义createApp创建至HTML或者Canvas或其他。

9,按需编译,体积更小,vue2编译后23K,Vue3 仅有10K。


#### 二:VUE经典面试题

1,谈谈你对MVVM的理解

即Model-View-ViewModel

Model指后端(服务器)的数据处理逻辑,对于前端就是服务端接口

View指浏览器渲染的视图,也就是HTML+CSS搭建的用户界面

View-Model就是Model和View的中间层,也就是视图数据层,在vue中即是script部分,在这一层,前端通过处理后端返回的数据进行处理,以期生成符合View需要的视图数据模型。ViewModel的数据模型包括了View的状态和行为两个部分,Model的数据模型仅包括状态。

MVVM实现了数据的双向绑定,开发者再也不需要低效频繁地操作DOM,在MVVM模型中,这一部分都已经处理好了,开发者只需要维护ViewModel中的代码,View就会自动地响应更新和变化,View也不会直接展示Model的数据,而是展示ViewModel处理后的数据,这样很好地解耦了View和Model层,这一环是前后端分离不可或缺的一环。

2,computed和watch的区别

Computed是依赖于其他数据进行处理然后返回新的数据,具有缓存性,当下一次获取computed的值的时候就会重新触发计算。

而watch类似于观察的作用,不具有缓存性,当检测其他数据的变化再执行逻辑或者数据操作,不会返回新数据。

当需要数据变化执行特性的逻辑动作,异步动作,或者比较大的性能开销的动作时,就可以使用watch,限制执行频率,减小性能开销。

3,为什么vue组件的data是一个函数

vue的组件是可以多处复用的,如果data直接就是一个对象的话,就会造成复用的组件重复使用了一个对象造成数据混乱,所以需要函数给每一个实例化组件生成一个独立的数据对象。

4,子组件为什么不可以修改props

vue官方提倡的是单向数据流,即父组件流向子组件,不提倡直接修改props, 直接修改父组件传过来的props会导致数据难以理解,维护困难。

5,什么是虚拟DOM

由于频繁操作真实DOM会产生重绘和回流,造成很大的性能开销,这时候虚拟DOM就应运而生了。

虚拟DOM的本质就是用JS对象去描述真实DOM,是真实DOM的抽象,当虚拟DOM更新,diff算法会同级比较虚拟DOM的差异,再调用Patch方法应用到真实DOM上。

使用key可以避免复用本来不同的DOM节点,导致更新同时影响多处,利用key还可以加快虚拟DOM遍历的速度,但是不建议使用index作为key, 这样简单的key会容易导致VUE错误地复用旧节点,产生各种古怪的问题。


## web前端性能优化

### 性能测量工具

1,Chrome devTool开发调试和性能评测

2,Lighthouse网站整体质量评估

3,WebPagetest多测试地点,全面性能报告

4,pageSpeed提供core web vitals新参数

瀑布图:它是一个级联图,每一行代表浏览器单独的一个请求;
fcp:浏览器第一次绘制非背景内容的时间不超过3s;
SI:平均加载页面资源所需的一个时间不超过3.3s;
fps:帧数1s 60fps
RALL 测量模型
响应:处理事件的时间必须在50ms
动画 每10ms 1帧
空闲:尽可能的增加空闲时间
加载:在5s之内完成加载并可以交互
LCP:衡量页面最大内容的加载速度不超过4s
FID:衡量访客首次响应速度不超过300ms
CLS:累计布局的偏移应小于0.25


DNS 深绿色 - 将域名转换成IP地址,在这个阶段,你可以处理的东西很少,但幸运的是,并非所有的请求都需要经过这一阶段。
Initial Connection [橙色]- 在浏览器发送请求之前,必须建立TCP连接,这个过程仅仅发生在瀑布图中的开头几行
SSL[紫色]- 如果你的页面是通过SSL/TLS这类安全协议加载资源,这段时间就是浏览器建立安全连接的过程。目前Google将HTTPS作为其搜索排名因素之一,SSL/TLS协商 的使用变得越来越普遍了。(TTFB)[绿色]-TTFB是浏览器请求发送到服务器的时间+服务器处理请求时间+响应报文的第一字节到达浏览器的时间,我们用这个指标来判断你的web服务器是否性能不够, 或者说你是否需要使用CDN。
Downloading(蓝色)- 这是浏览器用来下载资源所用的时间,你可以通过控制资源的大小来控制这段时间的长度。


现代浏览器是一个多进程的结构

### 浏览器的作用

将用户选择的web资源展现出来,它需要从服务器请求资源,并将其显示在浏览器窗口中,资源的格式通常是HTML或者XHTML,也包括PDF、Image和其他格式。用户通过URL来指定所请求资源的位置,通过DNS查询,将网址转换为IP地址

无非包含两大块 数据请求和数据处理(渲染)

### diff算法

功能:提升性能

虚拟dom ===>其实就是数据(把dom数据化)

主流:snabbdom、virtual-dom

snabbdom:https://www.npmjs.com/package/snabbdom

#### snabbdom搭建环境

npm init -y
cnpm install webpack@5 webpack-cli@3 webpack-dev-server@3-S
cnpm install snabbdom -S
新建webpack.config.js
配置webpack.config.js

```

面试总结

​ 1,能说一下http的强缓存和协商缓存吗

​ 2,Vue的响应式原理

​ 3,浏览器输入网址到页面加载经历了什么

​ 4,vue3 vue2有哪些区别

​ 5,vue生命周期有哪些

​ 6,webpack有哪些优化项目的方法(启动服务和打包)

​ 7,webpack中loader和plugins的区别

​ 8,优化页面加载速度慢的办法

​ 9,APP中嵌入网页加载慢怎么办

​ 10,你常用的CSS布局方式有哪些

​ 11,原生JS怎么给DOM添加点击事件

​ 12,http和https中的缓存通用吗

​ 13,vue中请求一般放在哪个声明周期(官方推荐mounted)

​ 14,css有哪些优化的方法

​ 15,缓存页面情况下如何让用户及时更新页面

​ 16,V-FOR可以 和V-IF一起用吗

  1. 获取DOM所有元素documents.all
  2. 获取树状的所有节点通过递归获取DOM,先从根元素开始遍历,再判断该元素是否存在child,再行递归。
  3. 什么是虚拟DOM参照上述
  4. VUE3 diff算法的变化增加了动态节点(patch flag)的标签,避免重复遍历(详见上)
  5. JS中广度遍历与深度遍历简单总结:广度优先是逐层遍历,深度优先是自上而下地遍历。详解:​ 广度:按照数据结构的层次一层一层地遍历,创建一个队列对其进行遍历,如果某一项存在子节点,则将子节点加入队列末端。当前节点在遍历时则会在队列中删除。(队列,先进先出)​ 深度:按照树形结构直接向下查找子节点,如果子节点不存在后再回溯到上一级节点的兄弟节点遍历,当不存在兄弟节点后再以此类推向上回溯,直至遍历完所有节点。(堆栈,后进先出,这里和堆栈并无关系)
  6. 数组去重的几种方式第一种:new Set去重第二种:两层循环法嵌套循环去重(判断当前是否已经存在,是的话splice)利用indexOf去重(arr.indexOf(item)===-1; arr.push(item))\利用sort排序,然后遍历和相邻元素对比使用includes(类似indexOf)filter,reduce去重如果还有对象或数组,查找是否存在不同键值,再行去重,如果有嵌套对象或数组,则需要递归去重,对比对象每一个键值(不可使用JSON.parse,如果对象键值一样但是顺序不一样,判断失败)
  7. 前端的几种设计模式结构型模式:​ 外观模式:将子系统api统一封装在高层接口中,以便于子系统更容易使用,例如JQuery、常见的框架和npm库等。​ 代理模式:增加对象的访问控制并添加额外的逻辑。例如Vue3的响应式原理。创建型模式:​ 工厂模式:将同一业务代码进行统一化,集中化的封装,避免代码混乱、重复的问题。(不再需要一个个引入构造函数,只需引入工厂对象即可)​ 单例模式:单例模式的class实例个数只能为1,当需要一个对象贯穿整个系统时才需要单例模式,且会引入全局状态,尽量避免使用。行为型模式​ 策略模式:当同一个行为在不同的场景中有不同的实现方式,就需要采取策略模式,例如登录可分为密码登录,验证码登录,第三方登录等,这些行为会配置一个统一的接口或者行为模式。方便在运行时切换策略,并且可避免大量的判断,不同策略可分开编写,便于维护。​ 迭代器模式:用于遍历容器中的元素,提供一致的遍历数据结构的方式,不用了解数据结构,提供遍历容器的能力且不用变更容器的接口。​ 观察者模式(订阅模式):当监测的对象变化时,通过调用观察者的方法将变化通知给观察者,例如Vue就应用了其作为响应式原理的一部分。​ 中介者模式:中介者(Mediator)包装了一系列对象的交互方式,使得这些对象不必直接交互二十由中介者进行处理交互,从而松散耦合。当某些对象的作用 发生变化时,不会立即影响另外的对象的作用,保证这些作用可以独立于彼此的变化。中介者和观察者有些类似,都是一对多和集中式通信,但是中介者只处理同级对象之间的变化,而观察者只处理观察者和对象之间的交互。​ 访问模式:访问者模式可以让我们在不改变对象结构的前提下给该对象新增不同的逻辑并保持在独立的访问者对象中,访问者模式常用于库和工具的拓展。
  8. JS的事件循环(见上)JS作为单线程语言,script在开始执行的时候整体作为一个宏任务开始执行,并在执行的时候将任务分为同步任务和异步任务,同步任务放在主线程中执行,异步任务会放在子线程中执行,不会影响主线程的执行栈。执行完的子任务将会放在任务队列中等待被主线程调用,当主线程执行栈执行完毕后,则会在任务队列中调取任务执行。而任务队列又分为宏任务和微任务,微任务会先于宏任务在执行栈中执行,宏任务会放在下一次的事件循环中执行。如此循环,就形成了事件循环。
标签: 前端 笔记 面试

本文转载自: https://blog.csdn.net/qq_38906008/article/details/137044138
版权归原作者 huahua1112520 所有, 如有侵权,请联系我们删除。

“前端笔记+面试”的评论:

还没有评论