写点什么

前端面经

作者:肥晨
  • 2022-11-03
    江苏
  • 本文字数:12752 字

    阅读完需:约 42 分钟

HTML、CSS 相关

HTML5 新特性

input 新增了一些属性:color-调色板、tel-电话、number-数字、date-年月日

video、aideo 视音频标签

语义化标签

语义化

语义化标签 : header、nav、main、footer

语义化的优点有:

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

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

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

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

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

盒模型

所有 HTML 元素都可以看做是一个作盒子,在 CSS 中,"box model"这一术语是用来设计和布局时使用。 

W3C 盒子模型(标准盒子模型):宽=内部宽度(content)+border+padding+margin

IE 盒子模型(怪异盒子模型):宽=内部宽度(content+border+padding)+margin

互换模型格式:

box-sizing:content-box;//变为标准盒模型(大部分浏览器默认)

box-sizing:border-box;//变为怪异盒子模型

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

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

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

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

 

区别:

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

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

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

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

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

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

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

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

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

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

不同点:

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

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

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

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 选择器

标签选择器

ID 选择器

类选择器

组选择器

通配符选择器

后代选择器

子元素选择器

伪类选择器

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: 此时可以操作 Dom

  • update 阶段:当 vue 实例里面的 data 数据变化时,触发组件的重新渲染 beforeUpdate updated

  • destroy 阶段:vue 实例被销毁 beforeDestroy:实例被销毁前,此时可以手动销毁一些方法 destroyed

data 为什么是一个函数而不是对象

因为对象是一个引用数据类型,如果 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,子->父 $on、$emit` 获取父子组件实例 parent、parent、children Ref 获取实例的方式调用组件的属性或者方法 Provide、inject` 官方不推荐使用,但是写组件库时很常用

兄弟组件通信

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

跨级组件通信

$attrs、$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 重新计算,从而致使它关联的组件得以更新。

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 的上面。

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

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 显示提交更改 state

  • Action 类似 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 推崇依赖前置,在定义模块的时候就要声明其依赖的模块

优点

用户体验好,因为没有延迟,依赖模块提前执行了。

CMD

CMD 是一个通用模块定义规范;是 SeaJs 推广过程中对模块定义的规范化产出

CMD 规范

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

优点

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

防抖节流

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

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

编码阶段

尽量减少 data 中的数据,data 中的数据都会增加 getter 和 setter,会收集对应的 watcher

v-if 和 v-for 不能连用

如果需要使用 v-for 给每项元素绑定事件时使用事件代理

SPA 页面采用 keep-alive 缓存组件

在更多的情况下,使用 v-if 替代 v-show

key 保证唯一

使用路由懒加载、异步组件

防抖、节流

第三方模块按需导入

长列表滚动到可视区域动态加载

图片懒加载

SEO 优化

预渲染

服务端渲染 SSR

打包优化

压缩代码

Tree Shaking/Scope Hoisting

使用 cdn 加载第三方模块

多线程打包 happypack

splitChunks 抽离公共文件

sourceMap 优化

用户体验

骨架屏

PWA

还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启 gzip 压缩等。


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

肥晨

关注

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

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

评论

发布
暂无评论
前端面经_前端面试题_肥晨_InfoQ写作社区