写点什么

2023 年超全前端面试题 - 背完稳稳拿 offer(欢迎补充)

作者:肥晨
  • 2023-04-20
    江苏
  • 本文字数:17176 字

    阅读完需:约 56 分钟

HTML、CSS 相关

HTML5

HTML5 新特性

  1. 增强了表单,input 新增了一些 type:

  2. color----定义调色板

  3. tel-----定义包含电话号码的输入域

  4. email---定义包含 email 地址的输入域

  5. search--定义搜索域

  6. number--定义包含数值的输入域

  7. date----定义选取日、月、年的输入域

  8. Video 视频标签;

  9. Audio 音频标签;

  10. 语义化标签;datatime-local

  11. 定义文档类型的简写<!DOCTYPE html>

  12. 地理定位;

  13. 拖放 api;

  14. Canvas 图形绘制;

  15. 富文本

  16. Web Storage:


localStorage:没有时间限制的数据存储 sessionStorage:在浏览器关闭的时候就会清除

语义化标签

首先是关于语义(Semantics)和默认样式的区别,默认样式是浏览器设定的一些常用 tag 的表现形式,<strong>语义化的主要目的就是让大家直观的认识标签(markup)和属性(attribute)的用途和作用</strong>,很明显看起来很像标题,因为拥有粗体和较大的字号。strong,em 用来区别于其他文字,起到了强调的作用。

语义化

语义化标签 : p、strong、em、footerhtml5 新出的语义化标签:


  • header(头部)

  • nav(导航)

  • section(主要用于对网站或应用程序中页面上的内容进行分块。)

  • article(一个页面的一部分,并且这部分专门用于独立的分类或复用)

  • aside(定义 article 以外的内容,aside 的内容应该与 article 的内容相关。表示当前页面或文章的附属信息部分)

  • footer(底部)

语义化的优点有:

(一)代码结构清晰,易于阅读,利于开发和维护


(二)方便设备解析根据语义渲染网页


(三)有利于(SEO)搜索引擎优化


(四)在浏览器 css 失效时,页面依然可读

如何语义化:

不用纯样式标签(b、i、u)、少用无语义标签(div、span)、使用语义化标签

盒模型

盒子模型是什么?

CSS 盒子模型(Box Model)。在所有的 HTML 元素都可以看成一个盒子;在 CSS 中,Box Model 这一术语被用来设计和布局中使用。


现在已知的有两种盒模型,W3C 盒模型(标准盒子)和 IE 盒子模型(怪异盒子)。

W3C 盒模型(标准盒子)和 IE 盒子模型(怪异盒子)分别是什么?

<strong>W3C 盒模型,也就是标准盒子模型</strong>

宽度=内容的宽度(content)+ border + padding + margin

<strong>IE 盒子模型,也就是怪异盒子模型</strong>

宽度=内容宽度(content+border+padding)+ margin

为什么叫 IE 盒子模型(怪异盒子)呢?

<em>是因为在低版本的 IE 中盒子使用的都是 border-box。</em>后期 IE5.5、IE6 及其以后,盒模型都为 content-box。注意:当浏览器未设置<!doctype>声明时,盒模型都为 border-box。


IE 盒子模型的范围也包括 margin、border、padding、content,和标准 W3C 盒子模型不同的是:IE 盒子模型的 content 部分包含了 border 和 pading。IE 盒模型 box-sizing: border-box。


通过 css3 的 box-sizing 属性,可以更改元素的盒子模型。标准 W3C 盒子模型的范围包括 margin、border、padding、content,并且 content 部分不包含其他部分。

box-sizing 属性?

用来控制元素的盒子模型的解析模式,默认为 content-box。互换模型格式。


<em>content-box</em>:W3C 的标准盒子模型,设置元素的 height/width 属性指的是 content 部分的高/宽


<em>border-box</em>:IE 传统盒子模型。设置元素的 height/width 属性指的是 border + padding + content 部分的高/宽

标签元素

行内元素、块级元素、空元素有哪些?区别?

1、行内元素:span、a、em、img、input


2、块级元素:div、ol、ul、form


3、空元素:br、hr、img、input


区别:


行内元素不换行、块级元素换行


正常情况下是块级元素包含行内元素,鲜少有行内元素包含块级元素


没有内容的标签称之为空元素,空元素是在开始标签中关闭的。

缓存

离线缓存与传统浏览器缓存的区别

离线缓存是整个应用,传统浏览器缓存是单个文件


离线缓存断网后依然可以打开页面,传统浏览器缓存不可以


离线缓断在有网络情况下优先使用缓存,传统浏览器缓存会通知网络更新缓存

本地存储

能不能说一说浏览器的本地存储?各自优劣如何?

浏览器的本地存储主要分为 Cookie、WebStorage, 其中 WebStorage 又可以分为 localStorage 和 sessionStorage。

cookie

cookie 数据始终在同源的 http 请求中携带,即 cookie 在浏览器和服务器间来回传递。cookie 数据不能超过 4K。cookie 容易被 XSS 攻击如果网站没有对用户输入进行过滤,就有可能造成 xss 攻击或者利用于 CSRF 攻击。

localStorage 和 sessionStorage

这两个 api 都是随着 html5 加入的新 api。这两者的主要区别是时效性不同,大小都为 5mb。主要是以 key、value 形式的字符串存储。


<b><em>缺点:</em></b>


localStorage 的缺点是,只能存入字符串,无法直接存储对象,所以我们每次存入和取出都要重新 json 处理一下...


而 JSON.stringify()我们知道,它对 undefined funtion 等一些类型无法正常处理,并且不能转换循环引用的对象,因此使用时需要注意.

<b><em>共同点: </em></b>

都会在浏览器端保存,有大小和同源限制

<b><em>不同点:</em></b>
  1. cookie 数据始终在同源的 http 请求中携带,即 cookie 在浏览器和服务器间来回传递。sessionStorage 和 localStorage 不会自动把数据发送给服务器,仅在本地保存。

  2. 存储大小限制也不同:cookie 数据不能超过 4K,sessionStorage 和 localStorage 可以达到 5M 或者更多

  3. 作用域不同:sessionStorage:仅在当前浏览器窗口关闭之前有效;localstorage:数据始终有效,窗口或浏览器关闭也一直保存,除非删除数据;cookie:在设置的 cookie 过期时间之前有效,即使窗口关闭或浏览器关闭

CSS

CSS 样式优先级

!important>style>id>class

BFC

BFC 即块格式化上下文。BFC 内的元素不会影响外面的元素。


创建:


1.float 不为 none


2.position 为 absolute 或 fixed


3.overflow 不为 visible


4.display 为 inline-block、flex、inline-flex 等


应用:


  1. 防止 margin 重叠

  2. 清除内部浮动

  3. 自适应多栏布局

三栏布局

绝对定位法:中间栏目使用 margin/padding 空出左右位置,左右使用绝对定位


浮动法:中间栏目使用 margin/padding 空出左右位置,左右使用浮动定位


Flex:flex:1


Grid:父元素:display:grid 子元素:grid-template-columns:100px auto 20px

CSS 选择器

  1. 标签选择器

  2. ID 选择器

  3. 类选择器

  4. 组选择器

  5. 通配符选择器

  6. 后代选择器

  7. 子元素选择器

  8. 伪类选择器

Flex:1 是什么

经常用于自适应布局。也就是 flex-grow、flex-shrink、flex-basis 的缩写

Display:flex 的常用属性

flex-drection:设置主轴方向


Justify-content:设置主轴排列方式


Flex-wrap:设置是否换行


Align-content/align-item:侧轴排列(多行/单行)

预处理器 less、sass

是 css 中一种抽象层。好处:


1.结构清晰、便于扩展


2.方便屏蔽浏览器的语法差异


3.多重继承

DOM、BOM 对象

BOM 是指浏览器对象模型,可以对浏览器窗口进行访问和操作。使用 BOM,开发者可以移动窗口、改变状态栏中的文本以及执行其他与页面内容不直接相关的动作。


DOM 是指文档对象模型,通过它,可以访问 HTML 文档的所有元素。 DOM 是 W3C 的标准。

浏览器渲染机制

网页生成过程:


1.HTML 被 HTML 解析器解析成 DOM 树


2.css 则被 css 解析器解析成 CSSOM 树


3.结合 DOM 树和 CSSOM 树,生成一棵渲染树(Render Tree)


4.生成布局(flow),即将所有渲染树的所有节点进行平面合成


5.将布局绘制(paint)在屏幕上

重排(也称回流)

当 DOM 的变化影响了元素的几何信息(DOM 对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。 触发:添加或者删除可见的 DOM 元素、元素尺寸改变——边距、填充、边框、宽度和高度

重绘

当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。 触发:改变元素的 color、background、box-shadow 等属性

重排重绘优化建议

1.样式表越简单,重排和重绘就越快。尽量用 class,少用 style 一条条改变样式


2.重排和重绘的 DOM 元素层级越高,成本就越高。如果可以灵活用 display,absolute,flex 等重排开销会比较小,或不会影响其他元素的重排。


3.使用虚拟 DOM 的脚本库。

JS 相关

js 数据类型、typeof、instanceof

  1. string、number、boolean、null、undefined、object(function、array)、symbol

  2. typeof 主要用来判断数据类型

  3. instanceof 判断该对象是谁的实例。

ES6

  1. 新增 symbol 类型 表示独一无二的值,用来定义独一无二的对象属性名;

  2. const/let 都是用来声明变量,不可重复声明,具有块级作用域。存在暂时性死区,也就是不存在变量提升。(const 一般用于声明常量);

  3. 变量的解构赋值(包含数组、对象、字符串、数字及布尔值,函数参数),剩余运算符(...rest);

  4. 模板字符串(${data});

  5. 扩展运算符(数组、对象);;

  6. 箭头函数;

  7. Set 和 Map 数据结构;

  8. Proxy/Reflect;

  9. Promise

ES6 里的 symble

它的功能类似于一种标识唯一性的 ID,每个 Symbol 实例都是唯一的。 Symbol 类型的 key 是不能通过 Object.keys()或者 for...in 来枚举的, 它未被包含在对象自身的属性名集合(property names)之中。 所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用 Symbol 来定义。

ES6 里的 set 和 map

Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数 Map 可以接受一个数组作为参数。Set 对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。

vue 的 key

1.key 的作用主要是为了高效的更新虚拟 DOM,其原理是 vue 在 patch 过程中通过 key 可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个 patch 过程更加高效,减少 DOM 操作量,提高性能。


2.另外,若不设置 key 还可能在列表更新时引发一些隐蔽的 bug


3. vue 中在使用相同标签名元素的过渡切换时,也会使用到 key 属性,其目的也是为了让 vue 可以区分它们, 否则 vue 只会替换其内部属性而不会触发过渡效果。

普通函数和箭头函数的区别

1.箭头函数是匿名函数,不能作为构造函数,不能使用 new


2.箭头函数不绑定 arguments,取而代之用 rest 参数...解决


3.箭头函数不绑定 this,会捕获其所在的上下文的 this 值,作为自己的 this 值


4.箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。


5.箭头函数不能当做 Generator 函数,不能使用 yield 关键字

闭包

闭包是指有权访问另一个函数作用域中的变量的函数 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行


闭包用途:能够访问函数定义时所在的词法作用域(阻止其被回收)

私有化变量

模拟块级作用域


创建模块


闭包缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏原型、原型链(高频)原型: 对象中固有的__proto__属性,该属性指向对象的 prototype 原型属性。


原型链: 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就是我们新建的对象为什么能够使用 toString()等方法的原因。


特点: JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

this 指向

代表的是当前上下文环境对象。在面向对象语言中 this 表示当前对象的一个引用。但在 JavaScript 中 this 是不固定的,它会随着环境的改变而改变。


  1. 在方法中,this 表示该方法所属的对象

  2. 在单独使用的情况下,this 代表的是全局对象

  3. 在函数中,this 表示的是全局对象

  4. 在函数中但是在严格模式下,this 为未定义(undefined)

  5. 在事件中,this 表示的是接收事件的元素


类似 call、apply 等方法可以改变 this 引用到的对象 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。apply 接收参数的是数组,call 接受参数列表,bind 方法传入一个对象。

new 关键字

1.首先创建了一个新的空对象


2.设置原型,将对象的原型设置为函数的 prototype 对象。


3.让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)


4.判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

作用域、作用域链

作用域负责收集和维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。(全局作用域、函数作用域、块级作用域)。 作用域链就是从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链。

继承(含 es6)、多种继承方式

(1)第一种是以原型链的方式来实现继承,但是这种实现方式存在的缺点是,在包含有引用类型的数据时,会被所有的实例对象所共享,容易造成修改的混乱。还有就是在创建子类型的时候不能向超类型传递参数。


(2)第二种方式是使用借用构造函数的方式,这种方式是通过在子类型的函数中调用超类型的构造函数来实现的,这一种方法解决了不能向超类型传递参数的缺点,但是它存在的一个问题就是无法实现函数方法的复用,并且超类型原型定义的方法子类型也没有办法访问到。


(3)第三种方式是组合继承,组合继承是将原型链和借用构造函数组合起来使用的一种方式。通过借用构造函数的方式来实现类型的属性的继承,通过将子类型的原型设置为超类型的实例来实现方法的继承。这种方式解决了上面的两种模式单独使用时的问题,但是由于我们是以超类型的实例来作为子类型的原型,所以调用了两次超类的构造函数,造成了子类型的原型中多了很多不必要的属性。


(4)第四种方式是原型式继承,原型式继承的主要思路就是基于已有的对象来创建新的对象,实现的原理是,向函数中传入一个对象,然后返回一个以这个对象为原型的对象。这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承,ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。


(5)第五种方式是寄生式继承,寄生式继承的思路是创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对象进行扩展,最后返回这个对象。这个扩展的过程就可以理解是一种继承。这种继承的优点就是对一个简单对象实现继承,如果这个对象不是我们的自定义类型时。缺点是没有办法实现函数的复用。


(6)第六种方式是寄生式组合继承,组合继承的缺点就是使用超类型的实例做为子类型的原型,导致添加了不必要的原型属性。寄生式组合继承的方式是使用超类型的原型的副本来作为子类型的原型,这样就避免了创建不必要的属性。

EventLoop

JS 是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,所以会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列。微任务队列的代表就是,Promise.then,MutationObserver,宏任务的话就是 setImmediate setTimeout setInterval

原生 ajax

ajax 是一种异步通信的方法,从服务端获取数据,达到局部刷新页面的效果。 过程:


  1. 创建 XMLHttpRequest 对象;

  2. 调用 open 方法传入三个参数 请求方式(GET/POST)、url、同步异步(true/false);

  3. 监听 onreadystatechange 事件,当 readystate 等于 4 时返回 responseText;

  4. 调用 send 方法传递参数。

事件冒泡、捕获(委托)

事件冒泡指在在一个对象上触发某类事件,如果此对象绑定了事件,就会触发事件,如果没有,就会向这个对象的父级对象传播,最终父级对象触发了事件。事件委托本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理。event.stopPropagation() 或者 ie 下的方法 event.cancelBubble = true; //阻止事件冒泡

Vue

简述 MVVM

MVVM 是 Model-View-ViewModel 缩写,也就是把 MVC 中的 Controller 演变成 ViewModel。Model 层代表数据模型,View 代表 UI 组件,ViewModel 是 View 和 Model 层的桥梁,数据会绑定到 viewModel 层并自动将数据渲染到页面中,视图变化的时候会通知 viewModel 层更新数据。

谈谈对 vue 生命周期的理解?

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


create 阶段:vue 实例被创建 beforeCreate: 创建前,此时 data 和 methods 中的数据都还没有初始化 created: 创建完毕,data 中有值,未挂载 mount 阶段: vue 实例被挂载到真实 DOM 节点 beforeMount:可以发起服务端请求,去数据 mounted: 此时可以操作 Domupdate 阶段:当 vue 实例里面的 data 数据变化时,触发组件的重新渲染 beforeUpdate updateddestroy 阶段:vue 实例被销毁 beforeDestroy:实例被销毁前,此时可以手动销毁一些方法 destroyeddata 为什么是一个函数而不是对象因为对象是一个引用数据类型,如果 data 是一个对象的情况下会造成所有组件共用一个 data。而当 data 是一个函数的情况下,每次函数执行完毕后都会返回一个新的对象,这样的话每个组件都会维护一份独立的对象(data)

computed 与 watch

watch 属性监听 是一个对象,键是需要观察的属性,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,监听属性的变化,需要在数据变化时执行异步或开销较大的操作时使用


computed 计算属性 属性的结果会被缓存,当 computed 中的函数所依赖的属性没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取。除非依赖的响应式属性变化时才会重新计算,主要当做属性来使用 computed 中的函数必须用 return 返回最终的结果 computed 更高效,优先使用


使用场景 computed:当一个属性受多个属性影响的时候使用,例:购物车商品结算功能 watch:当一条数据影响多条数据的时候使用,例:搜索数据

v-for 中 key 的作用

  1. key 的作用主要是为了更高效的对比虚拟 DOM 中每个节点是否是相同节点;

  2. Vue 在 patch 过程中判断两个节点是否是相同节点,key 是一个必要条件,渲染一组列表时,key 往往是唯一标识,所以如果不定义 key 的话,Vue 只能认为比较的两个节点是同一个,哪怕它们实际上不是,这导致了频繁更新元素,使得整个 patch 过程比较低效,影响性能;

  3. 从源码中可以知道,Vue 判断两个节点是否相同时主要判断两者的 key 和元素类型等,因此如果不设置 key,它的值就是 undefined,则可能永 远认为这是两个相同的节点,只能去做更新操作,这造成了大量的 dom 更新操作,明显是不可取的。

vue 组件的通信方式

父子组件通信

父->子 props,子->父 emit 获取父子组件实例 parent、parent、children Ref 获取实例的方式调用组件的属性或者方法 Provide、inject 官方不推荐使用,但是写组件库时很常用

兄弟组件通信

Event Bus 实现跨组件通信 Vue.prototype.$bus = new Vue() Vuex

跨级组件通信

listeners Provide、inject

路由传参

  1. 使用 router-link 进行路由导航,传递参数

  2. 直接调用 $router.push 实现携带参数的跳转

  3. 通过路由属性中的 name 来确定匹配的路由,通过 params 来传递参数

  4. 使用 path 来匹配路由,然后通过 query 来传递参数,这种情况下 query 传递的参数会显示在 url

路由的两种模式 hash 与 history

对于 Vue 这类渐进式前端开发框架,为了构建 SPA(单页面应用),需要引入前端路由系统,这也就是 Vue-router 存在的意义。前端路由的核心,就在于改变视图的同时不会向后端发出请求。1、hash ——即地址栏 URL 中的 #符号,它的特点在于:hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。


2、history ——利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录站,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改是,虽然改变了当前的 URL,但你浏览器不会立即向后端发送请求。history 模式,会出现 404 的情况,需要后台配置。

路由守卫

双向绑定实现原理当一个 Vue 实例创建时,Vue 会遍历 data 选项的属性,用 Object.defineProperty 将它们转为 getter/setter 并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

源码方面:vue 怎么优化重排重绘

Vue 通过使用 virtual DOM(虚拟 DOM)来优化重排重绘。virtual DOM 是一种技术,它可以比较新旧 DOM 树,以确定哪些变化需要应用到实际的 DOM 上。当 DOM 元素发生变化时,Vue 会自动检测并重新渲染 DOM,不会影响性能,从而使重绘和重排变得更加有效。


Vue 通过数据绑定来修改视图,当某个数据被修改的时候,set 方法会让闭包中的 Dep 调用 notify 通知所有订阅者 Watcher,Watcher 通过 get 方法执行 vm._update(...),进而去更新视图。


在 vm._update(...)中,Vue 会调用 patch(...),将新产生的 vnode 与数据未变更之前的旧的 prevVnode 进行对比。


在 patch(...)方法中,Vue 通过 sameVnode(...)对新旧两个 Vnode 对象进行比较,通过比对它们的标签名(tag)、键值(key)、是否都拥有 data 选项(isDef(data))以及是否都为注释节点(isComment)来确定这两个 Vnode 映射的真实 DOM 节点为同一个。只有确定它们是同一个真实 DOM 节点的不同映射时,才会执行下一步 patchVnode(...)。


在 patchVnode(...)中,Vue 会依据新旧两个 Vnode 对象是否拥有子节点 children 执行不同的 DOM 操作,当两个 Vnode 对象都有子节点时,会调用 updateChildren(...)方法递归的对子节点进行 patchVnode(...)。

v-model 的实现以及它的实现原理吗?

  1. vue 中双向绑定是一个指令 v-model,可以绑定一个动态值到视图,同时视图中变化能改变该值。v-model 是语法糖,默认情况下相于:value 和 @input。

  2. 使用 v-model 可以减少大量繁琐的事件处理代码,提高开发效率,代码可读性也更好

  3. 通常在表单项上使用 v-model

  4. 原生的表单项可以直接使用 v-model,自定义组件上如果要使用它需要在组件内绑定 value 并处理输入事件

  5. 我做过测试,输出包含 v-model 模板的组件渲染函数,发现它会被转换为 value 属性的绑定以及一个事件监听,事件回调函数中会做相应变量更新操作,这说明神奇魔法实际上是 vue 的编译器完成的。

new Vue 后整个的流程

initProxy:作用域代理,拦截组件内访问其它组件的数据。initLifecycle:建立父子组件关系,在当前组件实例上添加一些属性和生命周期标识。如[Math Processing Error]parent,parent,refs,$children,_isMounted 等。initEvents:对父组件传入的事件添加监听,事件是谁创建谁监听,子组件创建事件子组件监听 initRender:声明[Math Processing Error]slots 和 slots 和 createElement()等。initInjections:注入数据,初始化 inject,一般用于组件更深层次之间的通信。initState:重要)数据响应式:初始化状态。很多选项初始化的汇总:data,methods,props,computed 和 watch。initProvide:提供数据注入。思考:为什么先注入再提供呢??


1、首先来自祖辈的数据要和当前实例的 data,等判重,相结合,所以注入数据的 initInjections 一定要在 InitState 的上面。


  1. 从上面注入进来的东西在当前组件中转了一下又提供给后代了,所以注入数据也一定要在上面。

keep-alive 的实现

作用:实现组件缓存


钩子函数:activated 组件渲染后调用


deactivated 组件销毁后调用


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


配置属性:include 字符串或正则表达式。只有名称匹配的组件会被缓存


exclude 字符串或正则表达式。任何名称匹配的组件都不会被缓存


max 数字、最多可以缓存多少组件实例

vuex、vue-router 实现原理

vuex 是一个专门为 vue.js 应用程序开发的状态管理库。 核心概念:


state(单一状态树) getter/Mutation 显示提交更改 stateAction 类似 Mutation,提交 Mutation,可以包含任意异步操作。module(当应用变得庞大复杂,拆分 store 为具体的 module 模块)你怎么理解 Vue 中的 diff 算法?在 js 中,渲染真实 DOM 的开销是非常大的, 比如我们修改了某个数据,如果直接渲染到真实 DOM, 会引起整个 dom 树的重绘和重排。那么有没有可能实现只更新我们修改的那一小块 dom 而不要更新整个 dom 呢?此时我们就需要先根据真实 dom 生成虚拟 dom, 当虚拟 dom 某个节点的数据改变后会生成有一个新的 Vnode, 然后新的 Vnode 和旧的 Vnode 作比较,发现有不一样的地方就直接修改在真实 DOM 上,然后使旧的 Vnode 的值为新的 Vnode。


diff 的过程就是调用 patch 函数,比较新旧节点,一边比较一边给真实的 DOM 打补丁。在采取 diff 算法比较新旧节点的时候,比较只会在同层级进行。 在 patch 方法中,首先进行树级别的比较 new Vnode 不存在就删除 old Vnodeold Vnode 不存在就增加新的 Vnode 都存在就执行 diff 更新 当确定需要执行 diff 算法时,比较两个 Vnode,包括三种类型操作:属性更新,文本更新,子节点更新 新老节点均有子节点,则对子节点进行 diff 操作,调用 updatechidren 如果老节点没有子节点而新节点有子节点,先清空老节点的文本内容,然后为其新增子节点 如果新节点没有子节点,而老节点有子节点的时候,则移除该节点的所有子节点 老新老节点都没有子节点的时候,进行文本的替换


updateChildren 将 Vnode 的子节点 Vch 和 oldVnode 的子节点 oldCh 提取出来。 oldCh 和 vCh 各有两个头尾的变量 StartIdx 和 EndIdx,它们的 2 个变量相互比较,一共有 4 种比较方式。如果 4 种比较都没匹配,如果设置了 key,就会用 key 进行比较,在比较的过程中,变量会往中间靠,一旦 StartIdx>EndIdx 表明 oldCh 和 vCh 至少有一个已经遍历完了,就会结束比较。

浏览器从输入 url 到渲染页面,发生了什么?

三个方面:

网络篇:

  1. 构建请求

  2. 查找强缓存

  3. DNS 解析

  4. 建立 TCP 连接(三次握手)

  5. 发送 HTTP 请求(网络请求后网络响应)

浏览器解析篇:

  1. 解析 html 构建 DOM 树

  2. 解析 css 构建 CSS 树、样式计算

  3. 生成布局树(Layout Tree)

浏览器渲染篇:

  1. 建立图层树(Layer Tree)

  2. 生成绘制列表

  3. 生成图块并栅格化

  4. 显示器显示内容

  5. 最后断开连接:TCP 四次挥手

Http 和 Https 区别(高频)

1.HTTP 是不安全的,而 HTTPS 是安全的


2.HTTP 标准端口是 80 ,而 HTTPS 的标准端口是 443


3.HTTP 无法加密,而 HTTPS 对传输的数据进行加密


4.HTTP无需证书,而 HTTPS 需要 CA 的 SSL 证书

GET 和 POST 区别(高频)

1.GET 在浏览器回退不会再次请求,POST 会再次提交请求


2.GET 请求会被浏览器主动缓存,POST 不会,要手动设置


3.GET 请求参数会被完整保留在浏览器历史记录里,POST 中的参数不会


4.GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有限制


5.GET 参数通过 URL 传递,POST 放在 Request body 中


6.GET 参数暴露在地址栏不安全,POST 放在报文内部更安全


7.GET 一般用于查询信息,POST 一般用于提交某种信息进行某些修改操作


8.GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包

理解 xss,csrf,ddos 攻击原理以及避免方式

XSS(Cross-Site Scripting,跨站脚本攻击)是一种代码注入攻击。攻击者在目标网站上注入恶意代码,当被攻击者登陆网站时就会执行这些恶意代码,这些脚本可以读取 cookie,session tokens,或者其它敏感的网站信息,对用户进行钓鱼欺诈,甚至发起蠕虫攻击等。


CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

XSS 避免方式:

  1. url 参数使用 encodeURIComponent 方法转义

  2. 尽量不是有 InnerHtml 插入 HTML 内容

  3. 使用特殊符号、标签转义符。

CSRF 避免方式:

  1. 添加验证码

  2. 使用 token


服务端给用户生成一个 token,加密后传递给用户


用户在提交请求时,需要携带这个 token


服务端验证 token 是否正确

http 特性以及状态码

比如:


200响应成功
301永久重定向
302临时重定向
304资源缓存
403服务器禁止访问
404服务器资源未找到
500 502服务器内部错误
504 服务器繁忙
1xx Informational(信息状态码) 接受请求正在处理
2xx Success(成功状态码) 请求正常处理完毕
3xx Redirection(重定向状态码) 需要附加操作已完成请求
4xx Client Error(客户端错误状态码) 服务器无法处理请求
5xx Server Error(服务器错误状态码) 服务器处理请求出错复制代码
复制代码

http 如何实现缓存

  1. 强缓存==>Expires(过期时间)/Cache-Control(no-cache)(优先级高) 协商缓存 ==>Last-Modified/Etag(优先级高)Etag 适用于经常改变的小文件 Last-Modefied 适用于不怎么经常改变的大文件

  2. 强缓存策略和协商缓存策略在缓存命中时都会直接使用本地的缓存副本,区别只在于协商缓存会向服务器发送一次请求。它们缓存不命中时,都会向服务器发送请求来获取资源。在实际的缓存机制中,强缓存策略和协商缓存策略是一起合作使用的。浏览器首先会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。

什么是同源策略

一个域下的 js 脚本未经允许的情况下,不能访问另一个域下的内容。通常判断跨域的依据是协议、域名、端口号是否相同,不同则跨域。同源策略是对 js 脚本的一种限制,并不是对浏览器的限制,像 img,script 脚本请求不会有跨域限制。

前后端如何通信

Ajax : 短连接


Websocket : 长连接,双向的。


Form 表单:最原始的

跨域通信的几种方式

解决方案:


  1. jsonp(利用 script 标签没有跨域限制的漏洞实现。缺点:只支持 GET 请求)

  2. CORS(设置 Access-Control-Allow-Origin:指定可访问资源的域名)

  3. postMessage(message, targetOrigin, [transfer])(HTML5 新增 API 用于多窗口消息、页面内嵌 iframe 消息传递),通过 onmessage 监听 传递过来的数据

  4. Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。

  5. Node 中间件代理

  6. Nginx 反向代理

  7. 各种嵌套 iframe 的方式,不常用。

  8. 日常工作中用的最对的跨域方案是 CORS 和 Nginx 反向代理

前端工程化

webpack 配置,webpack4.0 有哪些优化点

module.exports={
entry: {},
output: {},
plugins: [],
module: [rules:[{}]]
}
复制代码

webpack 如何实现代码分离

1.入口起点:使用 entry 配置手动地分离代码。


2.防止重复:使用 CommonsChunkPlugin 去重和分离 chunk。


3.动态导入:通过模块的内联函数调用来分离代码。

常见的 Webpack Loader? 如何实现一个 Webpack Loader(NO)

loader: 是一个导出为函数的 javascript 模块,根据 rule 匹配文件扩展名,处理文件的转换器。


file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)


url-loader: 与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体)


image-loader:加载并且压缩图片文件


babel-loader:把 ES6 转换成 ES5


sass-loader:将 SCSS/SASS 代码转换成 CSS


css-loader:加载 CSS,支持模块化、压缩、文件导入等特性


style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS


postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀 eslint-loader:通过 ESLint 检查 JavaScript 代码


常见的 Webpack Plugin? 如何实现一个 Webpack Plugin(NO)plugin:本质是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。


html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)


uglifyjs-webpack-plugin:压缩 js 文件


clean-webpack-plugin:目录清除


mini-css-extract-plugin:分离样式文件,CSS 提取为独立文件,支持按需加载 (替代 extract-text-webpack-plugin)


loader 和 plugin 对比?Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。

前端模块化,CMD、AMD、CommonJS

CommonJS

CommonJS 是服务器端模块的规范,由 Node 推广使用,webpack 也采用这种规范编写


commonJs 规范:CommonJS 模块规范主要分为三部分:模块定义、模块标识、模块引用。


模块定义:module 对象:在每一个模块中,module 对象代表该模块自身。 export 属性:module 对象的一个属性,它向外提供接口。输出模块变量的最好方法是使用 module.exports 对象。一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为 global 对象的属性。


模块标识:传递给 require 方法的参数,必须是符合小驼峰命名的字符串,或者以 . 、.. 、开头的相对路径,或者绝对路径。


模块引用:加载模块使用 require(同步加载),该方法读取一个文件并执行,返回文件内部的 module.exports 对象。


优势:在后端,JavaScript 的规范远远落后并且有很多缺陷,这使得难以使用 JavaScript 开发大型应用。比如:没有模块系统、标准库较少、没有标准接口、缺乏包管理系统、列表内容


CommonJS 模块规范很好地解决变量污染问题,每个模块具有独立空间,互不干扰,命名空间相比之下就不太好。


CommonJS 规范定义模块十分简单,接口十分简洁。


CommonJS 模块规范支持引入和导出功能,这样可以顺畅地连接各个模块,实现彼此间的依赖关系


CommonJS 规范的提出,主要是为了弥补 JavaScript 没有标准的缺陷,已达到像 Python、Ruby 和 Java 那样具备开发大型应用的基础能力,而不是停留在开发浏览器端小脚本程序的阶段


缺点:没有并行加载机制


由于 CommonJS 是同步加载模块,这对于服务器端是很不好的,因为所有的模块都放在本地硬盘。等待模块时间就是硬盘读取文件时间,很小。但是,对于浏览器而言,它需要从服务器加载模块,涉及到网速,代理等原因,一旦等待时间过长,浏览器处于”假死”状态。


所以浏览器端不是很适合 Common.Js,出现另一种规范 AMD

AMD

AMD 是运行在浏览器环境的一个异步模块定义规范 ,是 RequireJS 在推广过程中对模块定义的规范化产出。


AMD 规范 AMD 推崇依赖前置,在定义模块的时候就要声明其依赖的模块


优点用户体验好,因为没有延迟,依赖模块提前执行了。CMDCMD 是一个通用模块定义规范;是 SeaJs 推广过程中对模块定义的规范化产出


CMD 规范 CMD 推崇依赖就近,只有在用到某个模块的时候才会去 require


优点性能好,因为只有用户需要的时候才执行。

防抖节流

函数防抖关注一定时间连续触发,只在最后执行一次,而函数节流侧重于一段时间内只执行一次。

防抖

//定义:触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。//搜索框搜索输入。只需用户最后一次输入完,再发送请求//手机号、邮箱验证输入检测 onchange oninput 事件//窗口大小 Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。


 const debounce = (fn, wait, immediate) => { let timer = null;  return function (...args) {  if (timer) clearTimeout(timer);   if (immediate && !timer) {      fn.call(this, args);   } timer = setTimeout(() => {   fn.call(this, args); }, wait); }; };const betterFn = debounce(() => console.log("fn 防抖执行了"), 1000, true);document.addEventListener("scroll", betterFn);
复制代码

节流

//定义:当持续触发事件时,保证隔间时间触发一次事件。//1. 懒加载、滚动加载、加载更多或监听滚动条位置;//2. 百度搜索框,搜索联想功能;//3. 防止高频点击提交,防止表单重复提交.


function throttle(fn,wait){    let pre = 0;     return function(...args){         let now = Date.now();         if( now - pre >= wait){             fn.apply(this,args);             pre = now;         }     } }function handle(){     console.log(Math.random()); }window.addEventListener("mousemove",throttle(handle,1000));
复制代码

对象深浅拷贝

//浅拷贝 1. Object.assign(target,source)2. es6对象扩展运算符。//深拷贝    function deepClone(obj) {      if (!obj || typeof obj !== "object") return;      let newObj = Array.isArray(obj) ? [] : {};      for (let key in obj) {        if (obj.hasOwnProperty(key)) {          newObj[key] = typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key];        }      }      return newObj;}
复制代码

数组去重,数组对象去重

//数组 const arr = [2,7,5,7,2,8,9]; console.log([...new Set(arr)]);// [2,7,5,8,9]; //对象 const list = [{age:18,name:'张三'},{age:18,name:'李四'},{age:18,name:'王五'}] let hash = {}; const newArr = arr.reduce((item, next) => {     hash[next.age] ? '' : hash[next.age] = true && item.push(next);     return item; }, []);console.log(list);
复制代码

数组扁平化

function flatten(arr) {       return arr.reduce((result, item) => {         return result.concat(Array.isArray(item) ? flatten(item) : item);       }, []); }
复制代码

性能优化

你都做过哪些 Vue 的性能优化?

移动端性能优化:

  1. 保持单个文件小于 25KB:移动网站页面要求下载资源,如果文件过大,会大大减慢页面加载速度。

  2. 打包内容为分段 multipart 文档:由于 HTTP 请求每一次都会执行三次握手,每次握手都会消耗较多的时间。使用 multipart,实现了多文件同时上传,可用一个 HTTP 请求获取多个组件。

图片优化:

  1. CSS sprites:俗称 CSS 精灵、雪碧图,雪花图等。即将多张小图片合并成一张图片,达到减少 HTTP 请求的一种解决方案。可通过 CSS 中的 background 属性访问图片内容。这种方案同时还可以减少图片总字节数,节省命名词汇量。

  2. 压缩图片:图片占据资源极大,因此尽量避免使用多余的图片,使用时选择最合适的格式大小,然后使用智图压缩,同时在代码中用 Srcset 来按需显示。(切记不要过分压缩 可能会导致图片迷糊)

  3. 尽量避免重设图片大小:重设图片大小是指在页面、CSS、JavaScript 等中多次重置图片大小,多次重设图片大小会引发图片的多次重绘,影响性能。

  4. 图片尽量避免使用 DataURL:DataURL 图片没有使用图片的压缩算法文件会变大,并且要解码后再渲染,加载慢耗时长。

  5. 图片懒加载:图片对页面加载速度影响非常大。比如,当一个页面内容比较多的时候,加载速度就会大大的降低,极大的影响到用户体验 。更有甚者,一个页面可能会有几百个图片,但是页面上仅仅只显示前几张图片,那其他的图片是否可以晚点加载用于提高性能。具体可见 >>

JavaScript 相关优化

  1. 把脚本放在页面底部:放在前面 js 加载会造成阻塞,影响后面 dom 的加载

  2. 使用外部 JavaScript 和 CSS :在现实环境中使用外部文件通常会产生较快的页面,因为 JavaScript 和 CSS 有机会被浏览器缓存起来。对于内联的情况,由于 HTML 文档通常不会被配置为可以进行缓存的,所以每次请求 HTML 文档都要下载 JavaScript 和 CSS。所以,如果 JavaScript 和 CSS 在外部文件中,浏览器可以缓存它们,HTML 文档的大小会被减少而不必增加 HTTP 请求数量。

  3. 压缩 JavaScript 和 CSS:压缩文件是为了降低网络传输量,减少页面请求的响应时间。

  4. 减少 DOM 操作:操作 dom 会产生几种动作,极大的影响渲染的效率。其中 layout(布局)和 paint(绘制)是最大的。

  5. js 开销缩短解析时间:开销:加载-》解析和编译-》执行 js 的解析和编译,执行要花很长时间(谷歌开发工具中的 performance 中可以查看。选中 main 主线程中的某一段。)解决方案:

  6. 代码拆分按需加载

  7. tree shaking 代码减重

  8. 避免长任务

  9. requestAnimationFrame 和 repuestIdleCallback 进行时间调度

  10. v8 编译原理(代码优化):

  11. 解析 js 代码成抽象语法树-》字节码-》机器码编译过程会进行优化运行时可能会发生反优化

  12. v8 内部优化脚本流:边下载边解析字节码缓存:常用的字节码会存起来(这个文件用到其他的文件也用到的参数)函数懒解析:先解析用到的

  13. 对象优化(迎合 v8 进行优化)保证对象初始化顺序一致(对象初始化时 v8 会生成隐藏属性以便后续复用并且是按照顺序排序的)不要直接赋值对象新属性(追加的属性需要通过描述数组间接查找)使用数组代替类数组(v8 会对数组进行优化)比如先将类数组转化成数组避免读取数组越界(比如 for 循环多查找 1 个下标会照成性能相差 6 倍)


造成 undefined 和数字比较数组也是对象,当找不到对应下标的时候回沿着原型链向上找造成额外开销业务上无效


  1. js 内存,避免造成内存泄漏:通过变量是否能被访问到来判断内存是否释放。


  • 局部变量: 函数执行完没有闭包引用会被标记回收

  • 全局变量: 直到浏览器被卸载页面释放

  • 回收机制:引用计数:每调用一次加一,当计数为 0 的时候进行回收。缺点是不能解决循环引用(例如 a 对象依赖于 b 对象,标记清除(垃圾回收): 从根节点去访问,当访问到不能被访问的对象就进行标记然后进行垃圾回收。(当 a 对象解决:避免意外的全局变量;避免反复运行引发的闭包;避免脱离的 dom 元素没有被回收(所以 react 有 ref 这个 api)。

CSS 相关优化

  1. 把样式表放在<head>标签中:css 放在 head 标签中比 css 放在 body 标签尾部少了一次构建 RenderTree, 一次计算布局和一次渲染网页, 因此性能会更好。

  2. 不要使用 CSS 样式表

  3. 使用<link>替代 @import

  4. 不要使用 filter

  5. 避免元素类型转化(数组中放多种类型不利于 v8 引擎优化代码)

  6. 降低 css 对渲染的阻塞(按需加载,放在 dom 前面加载)

  7. 利用 pu 完成动画(前面讲到的复合)

  8. 使用 contain 进行优化(优化强度大。例如: contan:layout 告诉浏览器这个节点内部的子元素和外面的使用 font-display 进行优化:让文字更早的显示在页面上,减轻文字闪动的问题

html 相关优化

  1. 减少 iframes 使用

  2. 压缩空白符

  3. 避免嵌套层次太深

  4. 避免使用 table 布局

  5. 减少没必要的注释

  6. 删除元素默认属性(比如默认 checkbox 等)

开发内容相关优化

  1. 减少 HTTP 请求数

  2. 减少 DNS 重定向

  3. 缓存 AJax 请求

  4. 延迟加载

  5. 预加载

  6. 减少 DOM 元素的数量

  7. 划分内容到不同域名

  8. 尽量减少使用 iframe

  9. 避免 404 错误

服务器相关优化

  1. 使用 CDN

  2. 添加 Expires 或 Cache-Control 响应头

  3. 启用 Gzip

  4. 配置 Etag

  5. 尽早输出缓冲

  6. Ajax 请求使用 GET 方法

  7. 避免图片 src 为空

  8. 传输加载优化服务器启用 gzip

  9. keep Alive(持久 TCP 连接)keepalive_requests 100;请求 100 次后开启 http 的 keepAlive 有 keepalive_timeout 65;65 秒后关闭。

  10. http 缓存最好是用 no-cache(要用的时候需要在服务器那边 Etag 验证下)

  11. service workers

  12. 加速重复访问

  13. 离线支持

Cookie 相关优化

  1. 减少 cookie 大小

  2. 静态资源使用无 cookie 域名

首屏加载优化

  1. 资源压缩、传输压缩、代码拆分、tree shaking、http 缓存

  2. 路由懒加载、预渲染、inlineCss、虚拟列表

  3. prefetch 和 preload 调整加载顺序 js 内存管理

发布于: 刚刚阅读数: 3
用户头像

肥晨

关注

还未添加个人签名 2021-04-15 加入

平台:InfoQ、阿里云、腾讯云、CSDN、掘金、博客园等平台创作者 领域:前端 公众号:农民工前端

评论

发布
暂无评论
2023年超全前端面试题-背完稳稳拿offer(欢迎补充)_三周年连更_肥晨_InfoQ写作社区