前端高频面试题总结

目录

vue相关面试题

Vue2和vue3的区别是什么

Vue 3.0 新增了两个钩子函数:onRenderTracked 和 onRenderTriggered

ES6相关面试题

js闭包

js防抖与节流

async、await 优缺点

new关键字

this指向问题

Call apply bind的区别

深拷贝与浅拷贝

事件委托

事件冒泡

原型与原型链

事件循环机制

垃圾回收机制

前端模块化

数组去重

JS原始数据类型有哪些?引用数据类型有哪些?

堆中的内存地址值

0.1+0.2为什么不等于0.3

[] == ![]结果是什么?为什么

作用域与与解析

Css面试题

水平垂直居中的方法

css溢出隐藏

Rem布局及其优缺点

px、em、rem、%、vw、vh、vm这些单位的区别

js常见机试题

分页懒加载

瀑布流

无限级目录树

前后端交互

三次握手

四次挥手

http协议

请求头

状态码

浏览器缓存机制

http长连接

Ajax封装

同异步上传下载

分片下载文件

跨域

观察者_单例_工厂_抽象工厂

策略_装饰器_代理_适配

手写源码

手写Promise92

手写vue

手写Vue3

手写VueRouter

手写Vuex

手写webpack

微前端qiankun

webpack打包全过程

首屏加载优化

Vue2和vue3的区别是什么

1.双向数据绑定原理不同

Vue2 的双向数据绑定是利用ES5的一个API  Object.defineProperty() 对数据进行劫持,结合发布订阅模式的方式来实现的。

Vue3 中使用ES6的Proxy API对数据代理。

Vue3 使用数据代理的优势有以下几点:1)defineProperty 只能监听某个属性,不能对整个对象进行监听 2)可以省去for in,闭包等内容来提升效率(直接绑定整个对象即可)3)可以监听数组,不用再单独的对数组做特异性操作,Vue3可以检测到数组内部数据的变化

2.是否支持碎片

Vue2 不支持碎片。Vue3 支持碎片,就是说可以拥有多个根节点

3.API 类型不同

Vue2 使用选项类型api,选项型api 在代码里分割了不同的属性:data,computed,method等。

Vue3 使用合成型api,新的合成型api 能让我们使用方法来分割,相比于旧的api 使用属性来分组,这样代码会更加简便和整洁。

283833922

4定义数据变量和方法不同

Vue2是把数据放到了data 中,在 Vue2中 定义数据变量是data(){},创建的方法要在method:{}

Vue3 就需要使用一个新的setup()方法,此方法在组件初始化构造的时候触发。使用以下三个步骤来建立反应性数据:1)从vue 引入 reactive;2)使用 reactive ()方法来声明数据为响应性数据;3) 使用setup()方法来返回我们的响应性数据,从而template 可以获取这些响应性数据。

5.生命周期钩子函数不同

Vue2 中的生命周期:beforeCreate 组件创建之前;created 组建创建之后;beforeMount 组件挂载到页面之前执行;Mounted 组件挂载到页面之后执行,beforeUpdate 组件更新之前;updated组件更新之后

Vue3 中的生命周期:setup 开始创建组件;onBeforeMount 组件挂载到页面之前执行;onMounted 组件挂载到页面之后执行;onBeforeUpdate 组件更新之前;onUpdated 组件更新之后;

而且 Vue3 生命周期在调用前需要先进行引入。除了这些钩子函数外,Vue3 还增加了 onRenderTracked 和onRenderTriggered 函数。

6.父子传参不同

Vue2 父传子,用props ;子传父用事件Emitting Events。在Vue2 中,会调用this$emit 然后传入事件名和对象。

Vue3 父传子,用props;子传父用Emitting Events 。在Vue3 中的setup()中的第一参数content 对象中就有 emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出emit 就可以在setup 方法中随意使用了。

7.指令与插槽不同

Vue2 中使用slot 可以直接使用slot ;v-for 与v-if 在Vue2中优先级高的是v-for 指令,而且不建议一起使用。

Vue3 中必须是使用v-slot的形式;vue 3中v-for 与v-if ,只会把当前v-if 当作v-for 的一个判断语句,不会相互冲突;

Vue3 中移除keyCode 作为v-on 的修饰符,当然也不支持config.keyCodes,取而代之的是使用键名来作为事件的修饰符来使用,于是Vue.config.keyCodes 也被弃用了-vue2我们在监听按键事件的时候,是可以通过在事件后面加上按键码来实现监听某一个按键的--mulingyuer.com/archives/831/# ;

​​​​​​​Vue 3.0 新增了两个钩子函数:onRenderTracked 和 onRenderTriggered

页面应用spa和多页面应用(mpa)区别

 

单页应用(SPA)

多页应用(MPA)

结构

一个主页面 + 许多模块的组件

许多完整的页面

资源文件(css,js)

组件公用的资源只需要加载一次

每个页面都要自己加载公用的资源

刷新方式

页面局部刷新

整页刷新

url模式

a.com/#/page1

a.com/page1.html

a.com/#/page2

a.com/page1.html

用户体验

页面切换快,体验佳;当初次加载文件过多时,需要做相关的调优。

页面切换慢,网速慢的时候,体验尤其不好

适用场景(SEO)

对体验度和流畅度有较高要求的应用,不利于 SEO(可借助 SSR 优化 SEO),适用于经常切换页面的场景和数据传递较多,多表单的场景

适用于对 SEO 要求较高的应用

过渡动画

Vue 提供了 transition 的封装组件,容易实现

很难实现

内容更新

相关组件的切换,即局部更新

整体 HTML 的切换,费钱(重复 HTTP 请求)

路由模式

可以使用 hash ,也可以使用 history

普通链接跳转

数据传递

因为单页面,使用全局变量就好(Vuex)

cookie 、localStorage 等缓存方案,URL 参数,调用接口保存等

相关成本

前期开发成本较高,后期维护较为容易

前期开发成本低,后期维护就比较麻烦,因为可能一个功能需要改很多地方

html文件请求

第一次进入页面的时候会请求一个html文件,刷新清除一下。切换到其他组件,此时路径也相应变化,但是并没有新的html文件请求,页面内容也变化了

每一次页面跳转的时候,后台服务器都会给返回一个新的html文档

首屏时间

首屏时间慢,首屏时需要请求一次html,同时还要发送一次js请求,两次请求回来了,首屏才会展示出来

首屏时间快,访问页面的时候,服务器返回一个 html,页面就会展示出来,这个过程只经历了一个HTTP请求

单页应用原理

js会感知到url 的变化,通过这一点,可以用js动态的将当前页面的内容清除掉,然后将下一个页面的内容挂载到当前页面上,这个时候的路由不是后端来做了,而是前端来做,判断页面到底是显示哪个组件,清除不需要的,显示需要的组件。这种过程就是单页应用,每次跳转的时候不需要再请求html文件了

单页应用开发中可能存在的问题

1、客户端支持。目前测试中发现部分 APP 尚未支持 hash 方式的返回。APP 测在 webview 的返回按钮上需要实现逻辑:如果不能后退,则关闭 webview;如果能后退,则后退。

2、页面状态保留。使用 react-router 时,切换页面不能保留页面的滚动高度。页面关闭后,上个页面被销毁(执行了 componentWillUnmount ),用户如果在上个页面操作到了底部再做跳转,则返回后会重新回到顶部。体验还是比不上 native,但是依然甩传统的页面跳转几条街。

3、页面带参数。原生的 query 参数应该在 # 之前,index.html?from=onlineSign#pageA。但是 # 后还是可以有参数,index.html#pageA?from=onlineSign ,这里的参数在 location.query 或者 location.search 中拿不到,但是可以在 router 中拿到。

ES6相关面试题

js闭包

闭包是什么?

闭包(closure)就是能够读取其他函数内部变量的函数。

在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成 “定义在一个函数内部的函数”。

在本质上,闭包是将函数内部和函数外部连接起来的桥梁。(闭包的最典型的应用是实现回调函数(callback) )。

闭包的作用: 

1. 在外部访问函数内部的变量

2. 让函数内的局部变量可以一直保存下去

3. 模块化私有属性和公共属性

参数和变量不会被垃圾回收机制回收

闭包的使用场景

1.定时器setTimeout

原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果。

2.回调

定义行为,然后把它关联到某个用户事件上(点击或者按键)。代码通常会作为一个回调(事件触发时调用的函数)绑定到事件。

3.函数防抖

js防抖与节流

防抖

在事件被触发 n 秒后再执行回调,如果该事件在这 n 秒内被重复触发,则重新计时(英雄回城,)

思路:通过闭包维护一个变量,此变量代表是否已经开始计时,如果已经开始计时则清空之前的计时器,重新开始计时

1.登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖

2.调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖

3.文本编辑器实时保存,当无任何更改操作一秒后进行保存

节流

节流:控制事件发生的频率,如控制为1s发生一次,甚至1分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 类似。

  1. scroll 事件,每隔一秒计算一次位置信息等
  2. 浏览器播放事件,每个一秒计算一次进度信息等
  3. input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖)

Promise async、await 优缺点

async 和 await 相比直接使用 Promise 来说,

优势在于处理 then 的调用链,能够更清晰准确的写出代码。

缺点在于滥用 await 可能会导致性能问题,因为 await 会阻塞代码(resolve(data); reject(err); ),也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性

new关键字

this指向问题

this指向:

1、全局上下文(函数外)

无论是否为严格模式,均指向全局对象。注意:严格模式下全局对象为 undifined

2、函数上下文(函数内)

默认的,指向函数的调用对象,且是最直接的调用对象:

简单调用,指向全局对象注意:严格模式下全局对象为 undifined ,某些浏览器未实现此标准也可能是 window

改变 this 指向的方式:

(1) new 关键字改变 this 指向

(2) call( )把b添加到第一个参数的环境中,简单来说,this就会指向那个对象

(3) apply( )apply方法和call方法有些相似,它也可以改变this的指向,也可以有多个参数,但是不同的是,第二个参数必须是一个数组

(4)bind( ) // bind方法返回的是一个修改过后的函数,

  // bind也可以有多个参数,并且参数可以执行的时候再次添加,但是要注意的是,参数是按照形参的顺序进行的。

Call apply bind的区别

bind和apply的区别

返回不同:bind返回是函数

参数不同:apply第二个参数必须是一个数组

apply(A,arguments),bind(A,args1,args2)

深拷贝与浅拷贝

浅拷贝:

把一个对象里面的每一个成员都复制一份放到新的对象里面

=> 两个对象是两个对象空间 ,互不影响

=> 通过 for in 循环来实现的

如果对象中的某一个成员的值是一个复杂数据类型的时候

=> 你浅拷贝完毕以后, 你操作这个复杂数据类型的成员, 依旧是在操作一个内容

深拷贝:

把一个对象里面的每一个成员都复制一份到新的对象里面

=> 当对象中的某一个成员是复杂数据类型的时候

=> 继续循环遍历这个复杂数据类型

=> 在新的对象里面也创建一个复杂数据类型继续复制进去

通过递归函数来实现深拷贝

=> 在函数内部判断, 如果你是一个对象或者数组

=> 那么就再次调用函数

这个问题通常可以通过 JSON.parse(JSON.stringify(object)) 来解决

事件委托

利用事件冒泡的原理,让自己所触发的事件,让它的父元素代替执行

事件冒泡

当一个元素接收到事件的时候会把他接收到的事件传给自己的父级,一直到window

原型与原型链

原型(proto) :每一个 javascript 对象在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型“继承”属性

原型链 (protoType:当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型和原型,一直找到最顶层为止

什么是原型:

Javascript规定,每一个函数都有一个prototype对象属性,指向另一个对象(原型链上面的)。

prototype(对象属性)的所有属性和方法,都会被构造函数的实例继承。这意味着,我们可以把那些不变(公用)的属性和方法,直接定义在prototype对象属性上。

prototype就是调用构造函数所创建的那个实例对象的原型(proto)。

prototype可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

为什么要用原型:使用原型对象解决浪费内存

你了解原型链吗 你能说说 prototype 与 proto 的区别吗?

1.对象有属性__proto__,指向该对象的构造函数的原型对象。

2.方法除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。

原型链的顶端是Object.prototype,

作用域链的中点是window, 

Object的原型是Function.prototype,

Object的原型的原型是

在数组原型链上实现删除数组重复数据的方法

ES6新特性

1. 可以使用let和const定义一个变量,都是块级作用域

2. 模板字符串(倒引号)

3. 解构赋值

4. 对象简写法

5. for...of循环,但是他不能循环对象

6. 展开运算符(...)他可以展开数组和对象的多个元素

7. 剩余参数(可变参数)

8. ES6箭头函数

9. 参数默认值

10.类和继承

11.模块化规范

12.用class定义一个类

13.新增了this的方法call(),apply()

14.新增了数组的方法forEach(),map(),filter(),reduce()

事件循环机制

JavaScript事件循环是一种处理异步事件和回调函数的机制,它是JavaScript实现异步编程的核心。它会不断地从任务队列中取出任务并执行,直到任务队列为空为止。事件循环中的任务分为同步任务和异步任务,异步任务又可以分为宏任务和微任务,微任务的执行优先级高于宏任务。

事件循环解决了什么问题?

JavaScript从诞生就是单线程。但是单线程就导致有很多任务需要排队,只有一个任务执行完才能执行后一个任务。如果某个执行时间太长,就容易造成阻塞;为了解决这一问题,JavaScript引入了事件循环机制

事件循环的应用场景

1. DOM 事件处理:通过监听 DOM 事件(例如 click、scroll 等),可以使用事件循环来异步更新 UI 或执行其他操作。

2. 定时器:使用 setTimeout() 和 setInterval() 函数可以创建定时器,用于在指定时间间隔之后执行相应的操作。这些操作会被作为异步任务添加到任务队列中等待执行。

3. 网络请求:当 JavaScript 需要发送网络请求时,可以使用 XMLHttpRequest 或 fetch API 发送异步请求,并将响应数据作为异步任务加入到任务队列中等待处理。

4. Promise 和 async/await:Promise 和 async/await 是 JavaScript 中常用的异步编程方式,实际上它们底层都是基于事件循环机制实现的。通过将回调函数封装为 Promise 对象或 async 函数,可以让异步代码更加易读、易维护。

5. Web Workers:Web Workers 可以让 JavaScript 在多线程环境下运行,从而避免阻塞主线程。Web Workers 使用了与事件循环类似的消息队列机制来实现异步通信。

macrotask宏任务

垃圾回收机制

GC(garbagecollection),GC执行时,中断代码,停止其他操作,遍历所有对象,对于不可访问的对象进行回收,在V8引擎中使用两种优化方法,

1分代回收,增量GC,目的是通过对象的使用频率,存在时长来区分新生代和老生代对象,多回收新生代区,少回收老生代区,减少每次遍历的时间,从而减少GC的耗时回收方法:

2引用计次,当对象被引用的次数为零时进行回收,但是循环引用时,两个对象都至少被引用了一次,因此导致内存泄漏,

在js中,如果一个对象不再被引用,那么这个对象就会被回收,

如果两个对象互相引用,而不在被第三者引用,那么这个互相引用的对象也会被回收。

前端模块化

前端模块化就是复杂的文件编程一个一个独立的模块,比如JS文件等等,分成独立的

模块有利于重用(复用性)和维护(版本迭代),这样会引来模块之间相互依赖的问题,

所以有了commonJS规范,AMD,CMD规范等等,以及用于JS打包(编译等处理)的

工具webpack

一个模块是能实现特定功能的文件,有了模块就可以方便的使用别人的代码,想要什么

功能就能加载什么模块。

CommonJS、AMD和CMD

CommonJS:开始于服务器端的模块化,同步定义的模块化,每个模块都是一个单独的

作用域,模块输出,modules.exports,模块加载require()引入模块。

AMD:中文名异步模块定义的意思。

requireJS实现了AMD规范,主要用于解决下述两个问题。

1.多个文件有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器

2.加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应的时间越长。

数组去重

/*

方法一:

双层循环,外层循环元素,内层循环时比较值

如果有相同的值则跳过,不相同则push进数组

*/

Array.prototype.distinct = function(){

 var arr = this,

  result = [],

  i,

  j,

  len = arr.length;

 for(i = 0; i < len; i++){

  for(j = i + 1; j < len; j++){

   if(arr[i] === arr[j]){

    j = ++i;

   }

  }

  result.push(arr[i]);

 }

 return result;

}

var arra = [1,2,3,4,4,1,1,2,1,1,1];

arra.distinct();    //返回[3,4,2,1]

/*

方法二:利用splice直接在原数组进行操作

双层循环,外层循环元素,内层循环时比较值

值相同时,则删去这个值

注意点:删除元素之后,需要将数组的长度也减1.

*/

Array.prototype.distinct = function (){

 var arr = this,

  i,

  j,

  len = arr.length;

 for(i = 0; i < len; i++){

  for(j = i + 1; j < len; j++){

   if(arr[i] == arr[j]){

    arr.splice(j,1);

    len--;

    j--;

   }

  }

 }

 return arr;

};

var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,];

var b = a.distinct();

console.log(b.toString()); //1,2,3,4,5,6,56

/*

优点:简单易懂

缺点:占用内存高,速度慢

方法三:利用对象的属性不能相同的特点进行去重

*/

Array.prototype.distinct = function (){

 var arr = this,

  i,

  obj = {},

  result = [],

  len = arr.length;

 for(i = 0; i< arr.length; i++){

  if(!obj[arr[i]]){ //如果能查找到,证明数组元素重复了

   obj[arr[i]] = 1;

   result.push(arr[i]);

  }

 }

 return result;

};

var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,];

var b = a.distinct();

console.log(b.toString()); //1,2,3,4,5,6,56

/*

方法四:数组递归去重

运用递归的思想

先排序,然后从最后开始比较,遇到相同,则删除

*/

Array.prototype.distinct = function (){

 var arr = this,

  len = arr.length;

 arr.sort(function(a,b){  //对数组进行排序才能方便比较

  return a - b;

 })

 function loop(index){

  if(index >= 1){

   if(arr[index] === arr[index-1]){

    arr.splice(index,1);

   }

   loop(index - 1); //递归loop函数进行去重

  }

 }

 loop(len-1);

 return arr;

};

var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,56,45,56];

var b = a.distinct();

console.log(b.toString());  //1,2,3,4,5,6,45,56

//方法五:利用indexOf以及forEach

Array.prototype.distinct = function (){

 var arr = this,

  result = [],

  len = arr.length;

 arr.forEach(function(v, i ,arr){  //这里利用map,filter方法也可以实现

  var bool = arr.indexOf(v,i+1);  //从传入参数的下一个索引值开始寻找是否存在重复

  if(bool === -1){

   result.push(v);

  }

 })

 return result;

};

var a = [1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,2,3,3,2,2,1,23,1,23,2,3,2,3,2,3];

var b = a.distinct();

console.log(b.toString()); //1,23,2,3

方法六:利用ES6的set

Set数据结构,它类似于数组,其成员的值都是唯一的。

利用Array.from将Set结构转换成数组

function dedupe(array){

 return Array.from(new Set(array));

}

dedupe([1,1,2,3]) //[1,2,3]

拓展运算符(...)内部使用for...of循环

1

2

3

let arr = [1,2,3,3];

let resultarr = [...new Set(arr)];

console.log(resultarr); //[1,2,3]

JS原始数据类型有哪些?引用数据类型有哪些?

 JS 中,存在着 7 种原始值,分别是: boolean  null undefined number string symbol bigint

引用数据类型: 对象Object(包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象- Date,数学函数-Math,函数对象-Function

堆中的内存地址值

 说出下面运行的结果,解释原因。

function test(person) {

    person.age = 26 

    person = {

    name: 'hzj',

    age: 18    

}

    return person 

    }

    const p1 = {

    name: 'fyq',

    age: 19 

    }

    const p2 = test(p1)

    console.log(p1) // -> ?

    console.log(p2) // -> ?

结果:

p1:{name: “fyq”, age: 26}

p2:{name: “hzj”, age: 18}

原因: 在函数传参的时候传递的是对象在堆中的内存地址值test函数中的实参personp1对象的内存地址,通过调用person.age = 26确实改变了p1的值,但随后person变成了另一块内存空间的 地址,并且在最后将这另外一份内存空间的地址返回,赋给了p2

0.1+0.2为什么不等于0.3

0.1和0.2在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出 现了精度的损失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会变成 0.30000000000000004

[] == ![]结果是什么?为什么 

解析:

== 中,左右两边都需要转换为数字然后进行比较。

[]转换为数字为0

![] 首先是转换为布尔值,由于[]作为一个引用类型转换为布尔值为true,

因此![]false,进而在转换成数字,变为0

0 == 0 , 结果为true

作用域与预解析

1. 全局作用域(Global Scope)

  在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:

(1)最外层函数和在最外层函数外面定义的变量拥有全局作用域.

  1. 所有末定义直接赋值的变量自动声明为拥有全局作用域
  2. 所有window对象的属性拥有全局作用域

2. 局部作用域(Local Scope) 

     和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所有在一些地方也会看到有人把这种作用域称为函数作用域

3. 块级作用域

        ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6 的到来,为我们提供了‘块级作用域’,可通过新增命令 let 和 const 来体现。

        块级作用域所声明的变量在指定块的作用域外无法被访问。块级作用域在如下情况被创建:

在一个函数内部,在一个代码块(由一对花括号包裹)内部

二、作用域链

  • JavaScript代码中至少有一个作用域, 即全局作用域。
  • 凡是代码中有函数,那么这个函数就构成另一个作用域。
  • 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。
  • 将这样的所有的作用域列出来,可以形成的结构就称之为作用域

三、预解析

JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。

JavaScript解析器执行JavaScript代码的时候,分为两个过程:预解析过程,代码执行过程

预解析过程:

    a、把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。

    b、把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。

c、先提升var,在提升function。

注意点:变量和函数同名时, 函数的优先级高 

https://blog.csdn.net/m0_64346035/article/details/124810358

预解析练习:

    var num = 123;

    fun();

    function fun() {

        console.log(num);//undefined

        var num = 666;

    }

    /*

    var num;

    function fun() {

        var num;

        console.log(num);

        num = 666;

    }

    num = 123;

    fun();

    */

Cookie session区别

1.  cookie数据存放在客户的浏览器上,session数据放在服务器上。

2.  cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗

考虑到安全应当使用session。

3.  session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE。

4.  单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个

cookie。

Cookie sessionStorage localStorage区别

cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间

来回传递cookie数据还有路径(path)的概念,可以限制。cookie只属于某个路径下

  1. 存储大小限制也不同

cookie数据不能超过4K,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据。

Storage虽然也有存储大小的限制,但是比cookie大得多,可以达到5M或更大

数据的

2有效期不同

sessionStorage:仅在当前的浏览器窗口关闭有效;

localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;

cookie:即使窗口和浏览器关闭只在设置的cookie过期时间之前一直有效

3作用域不同

sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;

localStorage:在所有同源窗口都是共享的;

cookie:也是在所有同源窗口中共享的

箭头函数的特点及用途



(1)简化语法:箭头函数可以用更短的语法定义函数,不需要使用function关键字和return语句。例如,(x, y) => x + y可以代替function(x, y) { return x + y; }。

(2)简化this绑定:在箭头函数中,this的值由外部最接近的非箭头函数的作用域决定,而不是动态绑定。这意味着在箭头函数中,this指向的是定义时的对象,而不是调用时的对象。这种特性可以避免在回调函数中出现this指向错误的问题。

(3)简化语法绑定:在箭头函数中,没有arguments对象。这意味着箭头函数不能通过arguments对象访问传递给函数的参数。如果需要访问传递的参数,可以使用剩余参数(rest parameters)或者使用展开语法(spread syntax)。

需要注意的是,箭头函数不能用作构造函数,不能使用new关键字实例化,并且没有prototype属性。箭头函数也没有自己的this、arguments、super或new.target绑定。

如何保存小数点的精度

(1)使用 toFixed() 方法:这个方法会将数字转换为字符串,并按照指定的小数位数四舍五入。

let num = 3.14159;

let fixedNum = num.toFixed(2); // "3.14",返回的是字符串

let fixedNumParsed = parseFloat(fixedNum); // 将字符串转换回数字

(2)使用乘除法手动四舍五入:先乘以10的指定小数位数的幂,然后进行四舍五入,最后再除以同样的幂。

function roundToDecimalPlaces(num, places) {

  const multiplier = Math.pow(10, places);

  return Math.round(num * multiplier) / multiplier;

}

 

let num = 3.14159;

let roundedNum = roundToDecimalPlaces(num, 2); // 3.14

(3)使用 Number.prototype.toPrecision():这个方法会按照指定的长度来四舍五入,如果设置的精度高于实际数字的小数位数,会用0来填充。

let num = 3.14159;

let precisionNum = num.toPrecision(2); // "3.14",同样返回的是字符串

let precisionNumParsed = parseFloat(precisionNum); // 转换回数字

注意:toFixed() 和 toPrecision() 返回的都是字符串形式的数字,如果需要数字类型,可以进一步转换

判断数据类型的几种方式

一、数据类型有那些

基本类型(简单类型/值类型/原始类型):Boolean、Number、String、undefined、null、Symbol(ES6 新增)

引用类型(复杂类型):Object、Array、Function

二、 数据类型判断方法

1、typeof

typeof 返回值 “object” 、“number”、“boolean”、“undefined”、“function” 、“string”、“function”、'symbol"

typeof 返回的值都是字符串类型

typeof 操作可以判断基本类型的数据,但是也存在一些特例,比如 typeof null 返回的是“object” ,因为 从逻辑上,null 这个特殊值被认为是一个对空对象的引用,表示一个空对象指针,实际上是基础类型

typeof 5          // "number"

typeof true       // "boolean"

typeof 'text'     // "string"

typeof Symbol(1)  // "symbol"

typeof undefined  // "undefined"

typeof null       // "object"

2、instanceof

instanceof 是用来 判断数据是否是某个对象的实例,返回一个布尔值

缺点

对于基本类型的数据,instanceof是不能直接判断它的类型的,因为实例是一个对象或函数创建的,是引用类型,所以需要通过基本类型对应的 包装对象 来判断。所以对于 null 和 undefined 这两个家伙就检测不了了~

5 instanceof Number // false

new Number(5) instanceof Number  // true

//因为原型链继承的关系,instanceof 会把数组都识别为 Object 对象,所有引用类型的祖先都是 Object 对象

3、constructor

使用 constructor 可以查看目标构造函数,也可以进行数据类型判断。但是不能判断 null 和 undefined,因为这两个特殊类型没有其对应的包装对象。constructor和instanceof 类似,constructor 返回结果的是自己的构造函数,而 instructor 则是自己与构造函数比较返回布尔值

(5).constructor === Number     // true

"text".constructor === String  // true

true.constructor === Boolean   // true

({}).constructor === Object    // true

console.log({} instanceof Object) // true

// Uncaught TypeError: Cannot read property 'constructor' of undefined

undefined.constructor === undefined  // 报错

null.constructor === null            // 报错

4、Object.prototype.toString

在判断数据类型时,我们称 Object.prototype.toString 为 “万能方法” “终极方法”,工作中也是比较常用而且准确

// 简单实现

var arr = [1, 2, 3]

Object.prototype.toString.call(arr)

// 简易实现 Array.isArray() 方法

function isArray(obj) {

  return type(obj) === 'array'

}

console.log(isArray(arr)) // true

在ECMAScript中,Object类型的每个实例都有 toString() 方法,返回对象的字符串表示,所以每个实例化的对象都可以调用 toString() 方法,并且其他数据类型也能使用 toString()

5、Array.isArray()

返回的是一个布尔值

let a = [1,2,3];

console.log(typeof a);  //返回“object”

console.log(Array.isArray(a));  //true

js判断空对象的几种方法

一、JSON.stringify()将对象转为字符串比较

var a={};
var b=new Object();
console.log(JSON.stringify(a)=="{}") //true
console.log(JSON.stringify(b)=="{}") //true

二、for…in循环
使用for in循环可以遍历所有属性以次判断对象是否为空对象:

var a={};
function isEmptyObject(obj){
for(var key in obj){
return false
};
return true
};
console.log(isEmptyObject(a));

三、Object.getOwnPropertyNames()
Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。用此方法判断空对象只需要判断返回的数组长度是否为零,为零的话就是空对象。

var obj = { };
console.log(Object.getOwnPropertyNames(obj).length == 0); // true

四、Object.keys()
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致。用此方法判断空对象只需要判断返回的数组长度是否为零,为零的话就是空对象。

var data = {};
var arr = Object.keys(data);
console.log(arr.length == 0);//true

 

Css面试题

水平垂直居中的方法

css溢出隐藏

单行

overflow: hidden; // 溢出隐藏

text-overflow: ellipsis; // 溢出用省略号显示

white-space: nowrap; // 规定段落中的文本不进行换行

多行

overflow: hidden; // 溢出隐藏

text-overflow: ellipsis; // 溢出用省略号显示

display: -webkit-box; // 作为弹性伸缩盒子模型显示。

-webkit-box-orient: vertical; // 设置伸缩盒子的子元素排列方式:从上到下垂直排列

-webkit-line-clamp: 3; // 显示的行数

Rem布局及其优缺点

首先Rem相对于根(html)的font-size大小来计算。简单的说它就是一个相对单例

如:font-size:10px;,那么(1rem=10px)

了解计算原理后首先解决怎么在不同设备上设置html的font-size大小。其实rem布局的本质是等比缩放,一般是基于宽度。

优点

可以快速适用移动端布局 字体图片 高度

缺点

①目前ie不支持,对pc页面来讲使用次数不多;

②数据量大:所有的图片,盒子都需要我们去给一个准确的值;才能保证不同机型的适

配;

③在响应式布局中,必须通过js来动态控制根元素font-size的大小。

也就是说css样式和js代码有一定的耦合性。且必须将改变font-size的代码放在css样

式之前。

px、em、rem、%、vw、vh、vm这些单位的区别

1、px

px就是pixel的缩写,意为像素。px就是一张图片最小的一个点,一张位图就是千千万万的这样的点构成的,比如常常听到的电脑像素是1024x768的,表示的是水平方向是1024个像素点,垂直方向是768个像素点。

2、em

参考物是父元素的font-size,具有继承的特点。如果自身定义了font-size按自身来计算(浏览器默认字体是16px),整个页面内1em不是一个固定的值。

3、rem

css3新单位,相对于根元素html(网页)的font-size,不会像em那样,依赖于父元素的字体大小,而造成混乱。

4、%

一般宽泛的讲是相对于父元素,但是并不是十分准确。

1、对于普通定位元素就是我们理解的父元素

2、对于position: absolute;的元素是相对于已定位的父元素

3、对于position: fixed;的元素是相对于 ViewPort(可视窗口)

3、vw

css3新单位,viewpoint width的缩写,视窗宽度,1vw等于视窗宽度的1%。

举个例子:浏览器宽度1200px, 1 vw = 1200px/100 = 12 px。

4、vh

css3新单位,viewpoint height的缩写,视窗高度,1vh等于视窗高度的1%。

举个例子:浏览器高度900px, 1 vh = 900px/100 = 9 px。

4、vm

css3新单位,相对于视口的宽度或高度中较小的那个。其中最小的那个被均分为100单位的vm

举个例子:浏览器高度900px,宽度1200px,取最小的浏览器高度, 1 vm = 900px/100 = 9 px。

由于现在vm的兼容性较差,这里就不做展示了。

BFC(块级格式化上下文)

BFC(块级格式化上下文),一个创建了新的 BFC 的盒子是独立布局的,盒子内元素的布局不会影响盒子外面的元素。在同一个 BFC 中的两个相邻的盒子在垂直方向发生 margin 重叠的问题 

BFC 是指浏览器中创建了一个独立的渲染区域,该区域内所有元素的布局不会影响到区域外元素的布局,这个渲染区域只对块级元素起作用 

BFC触发条件以及规则

触发条件:

1.float的值不为none

2.overflow的值不为visible

3.display的值为table-cell、tabble-caption和inline-block之一

4.position的值不为static或则releative中的任何一个

规则:

1.浮动的元素会被父级计算高度(父级触发了BFC)

2.非浮动元素不会覆盖浮动元素位置(非浮动元素触发了BFC)

3.margin不会传递给父级(父级触发了BFC),两个相邻元素上下margin会重叠(给其中一个元素增加一个父级,然后让他的父级触发BFC)

清除浮动;

非浮动元素盖住浮动元素,可依靠触发BFC来解决

margin合并情况,在其中一个元素父级触发BFC,将不会合并margin

Bfc的优点

清除元素之间的影响

清除内部浮动元素对父级元素的影响

创建自适应布局

Flex弹性布局常用属性

Flex 布局语法教程 | 菜鸟教程

display: flex;

  display: -webkit-flex; /* Safari Webkit内核的浏览器,必须加上-webkit前缀。*/

  display: flex;

注意,设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。

flex-direction: row | row-reverse | column | column-reverse;//排列方向

  • row(默认值):主轴为水平方向,起点在左端。
  • row-reverse:主轴为水平方向,起点在右端。
  • column:主轴为垂直方向,起点在上沿。
  • column-reverse:主轴为垂直方向,起点在下沿。

flex-wrap: nowrap | wrap | wrap-reverse;

  1. nowrap(默认):不换行。
  2. wrap:换行,第一行在上方。
  3. wrap-reverse:换行,第一行在下方。

flex-flow: <flex-direction> <flex-wrap>;

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

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

justify-content: flex-start | flex-end | center | space-between | space-around;

  • flex-start(默认值):左对齐
  • flex-end:右对齐
  • center: 居中
  • space-between:两端对齐,项目之间的间隔都相等。
  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

align-items属性定义项目在交叉轴上如何对齐。

align-items: flex-start | flex-end | center | baseline | stretch;

  • flex-start:交叉轴的起点对齐。
  • flex-end:交叉轴的终点对齐。
  • center:交叉轴的中点对齐。
  • baseline: 项目的第一行文字的基线对齐。
  • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

align-content: flex-start | flex-end | center | space-between | space-around | stretch;

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

  • flex-start:与交叉轴的起点对齐。
  • flex-end:与交叉轴的终点对齐。
  • center:与交叉轴的中点对齐。
  • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
  • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
  • stretch(默认值):轴线占满整个交叉轴。

Grid网格布局常用属性

设置为 grid 或 inline-grid 后,它就变成了一个网格容器

display: grid;

display: inline-grid;

通过 grid-template-columns 和 grid-template-rows 属性来定义网格中的列和行。

.grid-container { 

display: grid;

grid-template-columns: auto auto auto auto; //在网格容器中创建四个列:

grid-template-rows: 100px 300px;//在网格容器中设置行的高度

}

fr 单位

轨道可以使用任何长度单位进行定义。

网格引入了 fr 单位来帮助我们创建灵活的网格轨道。一个 fr 单位代表网格容器中可用空间的一等份。

以下实例定义了一个网格定义将创建三个相等宽度的轨道,这些轨道会随着可用空间增长和收缩。

grid-template-columns: 1fr 1fr 1fr;

column-gap

指定列之间的间隙

gap

row-gap 和 column-gap 的简写属性

grid

grid-template-rows, grid-template-columns, grid-template-areas, grid-auto-rows, grid-auto-columns, 以及 grid-auto-flow 的简写属性

grid-area

指定网格元素的名称,或者也可以是 grid-row-startgrid-column-startgrid-row-end, 和 grid-column-end 的简写属性

grid-auto-columns

指的默认的列尺寸

grid-auto-flow

指定自动布局算法怎样运作,精确指定在网格中被自动布局的元素怎样排列。

grid-auto-rows

指的默认的行尺寸

grid-column

grid-column-start 和 grid-column-end 的简写属性

grid-column-end

指定网格元素列的结束位置

grid-column-gap

指定网格元素的间距大小

grid-column-start

指定网格元素列的开始位置

grid-gap

grid-row-gap 和 grid-column-gap 的简写属性

grid-row

grid-row-start 和 grid-row-end 的简写属性

grid-row-end

指定网格元素行的结束位置

grid-row-gap

指定网格元素的行间距

grid-row-start

指定网格元素行的开始位置

grid-template

grid-template-rowsgrid-template-columns 和 grid-areas 的简写属性

grid-template-areas

指定如何显示行和列,使用命名的网格元素

grid-template-columns

指定列的大小,以及网格布局中设置列的数量

grid-template-rows

指定网格布局中行的大小

row-gap

指定两个行之间的间距

重绘和重排

重绘就是重新绘制(repaint):是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。

重排就是重新排列(reflow):当渲染树的一部分必须更新并且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。

JS数组常用方法

push、pop、unshift、shift、splice、slice、concat、join、revres、indexOf、sort、filter、map

vuex和storage存储数据的区别

  • (1)从存储的位置来说,Vuex 用的是内存,而 Storage 用的是文件存储;
  • (2)从易失性来说,Vuex与页面的生存周期相同(如关闭页面、刷新等数据就会消失),而
    localStorage是“永久”存储的,sessionStorage 生存于应用会话期间;
  • (3)从存储空间来看,Vuex取决于可用内存和浏览器的限制,Storage
    都有个默认的大小(至少5MB,由浏览器决定),超出大小则需要用户同意增加空间;
  • (4)从共享来看,Vuex无法跨标签页、跨物理页面共享,则Storage则可以在同一域名底下共享;
  • (5)从用途来看,Vuex是用于管理页面内容及组件的状态,而Storage主要是用于存储数据;
  • (6)Storage是由浏览器提供的基础设施,而Vuex则是由JavaScript程序库提供的服务

echarts的基本使用,地图相关

Pina,ps基本画图

H5新特性

  • 语义标签
  • 增强型表单

  三、视频和音频

  四、Canvas绘图

  五、SVG绘图

  六、地理定位

  七、拖放API

  八、WebWorker

  九、WebStorage

  十、WebSocket

前后端交互

三次握手

客服端发c起请求连接服务器端s确认,服务器端也发起连接确认客服端确认。 第一次握手:客端发送一个请求连接,服务器端只能确认自己可以接受客服端发送的报文段

第二次握手: 服务端向客服端发送一个链接,确认客服端收到自己发送的报文段

第三次握手: 服务器端确认客服端收到了自己发送的报文段

简写:

第一次:客户端向服务端发送一个请求,服务端确认接收

第二次:服务端发送向客户端发送一个链接,确认已经接收到报文

第三次:服务端确认客户端已经接收到报文

四次挥手

  • 客户端 -- FIN --> 服务端, FIN—WAIT
  • 服务端 -- ACK --> 客户端, CLOSE-WAIT
  • 服务端 -- ACK,FIN --> 客户端, LAST-ACK
  • 客户端 -- ACK --> 服务端,CLOSED

第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。

即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。

第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。

即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。

第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。

即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。

第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。

即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。

七层网络结构

1.物理层:使用一定的物理介质(光纤、网线wifi) 01 信号

2.数据链路层:MAC地址在数据链路层

3.网络层:IP协议

4.传输层:UDP(用户数据包协议)/TCP(传输控制协议)

其中UDP协议的特点: 只管发,不管收 DNS。什么情况下用到了UDP协议:微信电话 、流媒体

TCP协议的特点

- 重传机制

- 排序机制–根据数据包的编号对数据进行排序,重组数据包。保证数据包的完整性和准确性。

5.会话层 断点续传

6.表示层 翻译 解决不同系统之间数据传输的问题

7.应用层 HTTP协议

什么是四层网络结构呢:

物理层 数据链路层

网络层

传输层

会话层、表示层、应用层

http协议和https协议

概念

https的SSL加密是在传输层实现的。

http: 超文本传输协议,是互联网上应用最为广泛的一种网络协议,是一个客户端和服

务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传

输协议,它可以使浏览器更加高效,使网络传输减少。

https: 是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL

层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。

https协议的主要作用是:建立一个信息安全通道,来确保数组的传输,确保网站的真实性。

区别

http传输的数据都是未加密的,也就是明文的,网景公司设置了SSL协议来对http协议

传输的数据进行加密处理,简单来说https协议是由http和ssl协议构建的可进行加密传

输和身份认证的网络协议,比http协议的安全性更高。

主要的区别如下:

  1. Https协议需要ca证书,费用较高。
  2. http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  3. 使用不同的链接方式,端口也不同,一般而言,http协议的端口为80,https的端口为443
  4. http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

https协议的工作原理

客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。

客户使用https url访问服务器,则要求web  服务器建立ssl链接。

web服务器接收到客户端的请求之后,会将网站的证书(证书中包含了公钥),返回或

者说传输给客户端。

客户端和web服务器端开始协商SSL链接的安全等级,也就是加密等级。

客户端浏览器通过双方协商一致的安全等级,建立会话密钥,然后通过网站的公钥来加

密会话密钥,并传送给网站。

web服务器通过自己的私钥解密出会话密钥。

web服务器通过会话密钥加密与客户端之间的通信。

https协议的优点

使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;

HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比

http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。

HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻

击的成本。

谷歌曾在2014年8月份调整搜索引擎算法,并称“比起同等HTTP网站,采用HTTPS

加密的网站在搜索结果中的排名将会更高”。

https协议的缺点

https握手阶段比较费时,会使页面加载时间延长50%,增加10%~20%的耗电。

https缓存不如http高效,会增加数据开销。

SSL证书也需要钱,功能越强大的证书费用越高。

SSL证书需要绑定IP,不能再同一个ip上绑定多个域名,ipv4资源支持不了这种消耗。

get和post 的区别

get请求:从指定的资源请求数据,用于获取数据,一般用于搜索排序和筛选之类的操作。

post请求:向指定的资源提交要被处理的数据,用于将数据发送给服务器,一般用于修改和写入数据。

get请求和post请求本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

区别

(1)post请求更安全(不会作为url的一部分,不会被缓存、保存在服务器日志、以及浏览器浏览记录中,get请求的是静态资源,则会缓存,如果是数据,则不会缓存)

(2)post请求发送的数据更大(get请求有url长度限制,http协议本身不限制,请求长度限制是由浏览器和web服务器决定和设置)

(3)post请求能发送更多的数据类型(get请求只能发送ASCII字符)

(4)传参方式不同(get请求参数通过url传递,post请求放在request body中传递)

(5)get请求的是静态资源,则会缓存,如果是数据,则不会缓存

(6)get请求产生一个TCP数据包;post请求产生两个TCP数据包(get请求,浏览器会把http header和data一并发送出去,服务器响应200返回数据;post请求,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 返回数据)

注意:在发送 POST 的时候都没有带 Expect 头,server 也自然不会发 100 continue。

post请求的过程

(1)浏览器请求tcp连接(第一次握手)

(2)服务器答应进行tcp连接(第二次握手)

(3)浏览器确认,并发送post请求头(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)

(4)服务器返回100 Continue响应

(5)浏览器发送数据

(6)服务器返回200 OK响应

get请求的过程

(1)浏览器请求tcp连接(第一次握手)

(2)服务器答应进行tcp连接(第二次握手)

(3)浏览器确认,并发送get请求头和数据(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)

(4)服务器返回200 OK响应

网络环境好的情况下,发一次包和发两次包的时间差别基本可以忽略。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。

请求头

状态码

浏览器缓存机制

http长连接

Ajax封装

同异步上传下载

分片下载文件

跨域

同源策略: 限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互

一个源指的是主机名,协议和端口号的组合,必须相同

(1)JSONP:通过动态创建script,再请求一个带参网址实现跨域通信。

(2)document.domain+iframe跨域:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

(3)location.hash+iframe跨域:a欲与b跨域相互通信,通过中间页c来实现三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

(4)window.name+iframe跨域:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。

(5)postMessage跨域:可以跨域操作的window属性之一。

(6)CORS:服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求,前后端都需要设置。

(7)代理(proxy)跨域:启一个代理服务器,实现数据的转发

观察者_单例_工厂_抽象工厂

策略_装饰器_代理_适配

微前端qiankun

https://zhuanlan.zhihu.com/p/414468874

前端是什么

一个应用,当不断迭代的时候,功能会越来越多,代码量随着也会变得越来越大。进而代码之间的耦合性会变高,这样导致开发和维护很糟心,牵一发而动全身。于是有了微前端来解这个问题,按功能可以将这个应用拆分成多个项目,每个项目都是独立的仓库,独立的部署,然后利用微前端再组合成在一起。

微前端是一种由独立交付的多个前端应用组成整体的架构风格。具体的,将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的小块,而在用户看来仍然是内聚的单个产品。

乾坤

乾坤是基于single-spa单页面应用框架的,而single-spa是一个将多个单页面应用聚合为一个整体应用的 JavaScript 微前端框架,而乾坤在single-spa的基础上主要做了资源的加载和应用之间的隔离。

qiankun框架中微应用之间如何跳转

qiankun框架提供两种跳转方式:

1、通过history.pushState()方式进行跳转

<button onClick={() => {

    window.history.pushState({

       user: {......}

     }, '', '/app1')}

 }>跳转</button>

2、将主应用的路由实例传递给子应用,子应用使用主应用实例进行跳转。

微应用需要支持跨域访问

  遇到这样报错:Access to fetch at ‘http://localhost:3000/’ from origin ‘http://localhost:8000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

  由于qiankun框架 解析微应用使用 import-html-entry库通过fetch请求相关资源,所以需要微应用支持跨域访问;在webpack devServer中加入以下代码即可

headers: {

'Access-Control-Allow-Origin': '*'

},

微应用对应生命周期钩子函数具体是做啥的

1、遇到报错:loader.js:220 Uncaught (in promise) Error: [qiankun] You need to export lifecycle functions in app4 entry

  此问题是由于微应用中没有暴露qiankuan的生命周期;需要在微应用工程中加入相关的生命周期函数;具体位置应为微应用中webpack的entry 值指向的js文件中添加即可;

2、qiankun 各生命周期钩子是做什么的

(1)bootstrap:只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。

(2)mount:应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法。

(3)unmount:应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例。

沙盒

沙盒主要作用就是隔离微应用之间的脚本和样式影响,需要处理style、link、script类型的标签。对于处理的时机第一个是在首次加载的时候,第二个是在微应用运行中。在运行中的处理方案就是乾坤重写了下面这些原生的方法,这样就可以监听到新添加的节点,然后对style、link、script标签进行处理。

脚本隔离

脚本隔离的方式主要有两种,一种是快照拷贝的方式,一个是基于proxy的方式。乾坤会根据当前环境是否支持proxy来决定用那种方式。

1.快照方式

在创建微应用的时候会实例化一个沙盒对象,它有两个方法,active是在激活微应用的时候执行,而inactive是在离开微应用的时候执行。

整体的思路是在激活微应用时将当前的window对象拷贝存起来,然后从modifyPropsMap中恢复这个微应用上次修改的属性到window中。在离开微应用时会与原有的window对象做对比,将有修改的属性保存起来,以便再次进入这个微应用时进行数据恢复,然后把有修改的属性值恢复到以前的状态。

2.proxy方式

乾坤中关于proxy的隔离方式有两种,我们以最优的方案分析。

微应用中的script内容都会加with(global)来执行,这里global是全局对象,如果是proxy的隔离方式那么他就是下面新创建的proxy对象。我们知道with可以改变里面代码的作用域,也就是我们的微应用全局对象会变成下面的这个proxy。当设置属性的时候会设置到proxy对象里,在读取属性时先从proxy里找,没找到再从原始的window中找。也就是你在微应用里修改全局对象的属性时不会在window中修改,而是在proxy对象中修改。因为不会破坏window对象,这样就会隔离各个应用之间的数据影响。

样式隔离

默认情况下沙箱可以确保单实例场景子应用之间的样式隔离(切换子应用时会卸载添加的样式标签),但是无法确保主应用跟子应用、或者多实例场景的子应用样式隔离,需要手动增加配置参数才能激活下面的隔离。

1.域隔离

为每个css规则添加特定的前缀来起到隔离的作用,例如微应用中的样式是p{color:#000},处理后为.app1 p {color:#000} 。

  • 创建一个临时的style节点用来后续处理
  • 调用process来处理style规则
  • 通过style的sheet属性来获取一条条规则
  • 然后调用ruleStyle进行转化,转化是通过正则进行匹配然后替换
  • 最后把转化后的内容替换到原有的style节点中

2. shadow dom

关于shadow dom的使用点这里,简单讲就是在当前微应用的根节点开启shadow,然后子节点的操作都是在shadowRoot上来进行隔离。

webpack打包全过程

webpack用来干什么的

webpack是一个现代 JavaScript 应用程序的静态模块打包器(modulebundler)。当

webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependencygraph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。

过程:

1.初始化参数阶段。

这一步会从我们配置的webpack.config.js中读取到对应的配置参数和shell命令中传入的参数进行合并得到最终打包配置参数。

2.开始编译准备阶段

这一步我们会通过调用webpack()方法返回一个compiler方法,创建我们的compiler对象,并且注册各个Webpack Plugin。找到配置入口中的entry代码,调用compiler.run()方法进行编译。

3.模块编译阶段

从入口模块进行分析,调用匹配文件的loaders对文件进行处理。同时分析模块依赖的模块,递归进行模块编译工作。

4.完成编译阶段

在递归完成后,每个引用模块通过loaders处理完成同时得到模块之间的相互依赖关系。

5.输出文件阶段

整理模块依赖关系,同时将处理后的文件输出到ouput的磁盘目录中。

webpack和gulp区别(模块化与流的区别)

gulp强调的是前端开发的工作流程,我们可以通过配置一系列的task,定义task处理的

事务(例如文件压缩合并、雪碧图、启动server、版本控制等),然后定义执行顺序,

来让gulp执行这些task,从而构建项目的整个前端开发流程。

webpack是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源(图

片、js文件、css文件等)都看成模块,通过loader(加载器)和plugins(插件)对资源进行处理,打包成符合生产环境部署的前端资源。

首屏加载优化

https://blog.csdn.net/qq_64760783/article/details/131644022

首屏加载慢问题分析

首屏在一些必须的文件都加载成功后才开始进行渲染,首屏加载慢的主要耗时就在加载这些必须的文件上,这些必须的文件是

js/app.d796800d.js

js/chunk-vendors.e95de2cc.js

css/app.69fa25fa.css

css/chunk-vendors.53794358.css

解决方案:解决方案就是加快加载这些必须文件

一、路由懒加载

1、使用 vue-router 懒加载解决首次加载时资源过多导致的速度缓慢问题

路由懒加载是一种按需要加载的方式,即需要时再加载。优化方案是将直接导入js变成按需要导入js。app.js中的页面拆分成单独的js文件,按需加载,加速app.js文件的下载速度从而减少首页加载时间。

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。

二、CDN加速

在打包后发现chunk-vendor.js 文件占用内存特别大,这里面主要是使用的一些第三方库,例如 vue-router,axios,elementUI ,echarts等文件。

通过在index.html 中直接引入第三方资源来缓解我们服务器的压力,其原理是将我们的压力分给了其他服务器站点。

推荐外部的库文件使用CDN资源:

bootstrap CDN:https://www.bootcdn.cn

Staticfile CDN:https://www.staticfile.org

jsDelivr CDN:https://www.jsdelivr.com

75 CDN:https://cdn.baomitu.com

UNPKG:https://unpkg.com

cdnjs:https://cdnjs.com

以引入vue、vuex、vue-router为例:

第一步 在index.html中引入第三方库:

三、图片资源压缩以及使用图片懒加载

对于图片较多的页面来说,图片的大小对于页面加载速度的影响十分明显。所以在项目上线之前,一般都要将图片压缩一下

CSS雪碧图 :使用雪碧图可以把多个图片整合到一张图片中,减少HTTP请求次数。但是当整合图片比较大时,一次加载会比较慢,加载失败会导致多个位置的图片无法正常显示。

转换成base64:将图片数据编码成串字符串,来代替原本图像的地址,图片不需要再进行http请求。(缺点:无法缓存;转换后会导致CSS文件体积增大,渲染时长时间出现空白屏幕,影响用户体验,而且只可以转化小图片,图片过大不能转)

SVG矢量图:常用于存储图标,只加载一次,图片不需要再进行http请求。图片放大也不会模糊

四、防止编译文件中出现map文件

在 config/index.js 文件中将productionSourceMap的值设置为false.

五、gzip压缩

前端配置gzip压缩,并且服务端使用nginx开启gzip,用来减小网络传输的流量大小。

第一步

命令行执行:npm i compression-webpack-plugin -D

第二步

在webpack的dev开发配置文件中加入如下代码:

const CompressionWebpackPlugin = require('compression-webpack-plugin')

plugins: [

   new CompressionWebpackPlugin()

]

六、前端代码优化

合理使用v-if和v-show

使用定时器和回调函数等记得销毁

避免意外的全局变量

适当使用闭包避免内存泄漏

  1. 前端做一些接口缓存:前端也可以做接口缓存,缓存的位置有两个,一个是内存,即赋值给运行时的变量,另一个是localStorage。
  2. 页面使用骨架屏

js常见机试题

分页懒加载

瀑布流

无限级目录树

手写源码

手写Promise92

手写vue

手写Vue3

手写VueRouter

手写Vuex

手写webpack

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值