输入网址后,http的过程
1、使用DNS域名解析
2、发起TCP的3次握手
3、建立TCP连接后发起http请求;
4、服务器响应http请求,浏览器得到返回response;
5、浏览器解析response,并请求其它的资源(如js、css、图片等);
6、浏览器对页面进行渲染。
浏览器渲染过程
1、解析HTML生成DOM树:浏览器开始解析HTML文档,生成DOM树。DOM树是由HTML元素以及它们的属性和嵌套关系构成的。
2、解析CSS生成CSSOM树:同时,浏览器解析CSS代码,生成CSSOM树。CSSOM树表示了CSS规则以及它们应用的顺序,它的结构类似于DOM树。
3、合并DOM树和CSSOM树生成渲染树:浏览器将DOM树和CSSOM树合并成一个渲染树。渲染树只包含需要显示在页面上的节点,不显示的节点(如display: none的节点)不会被包含在渲染树中。
布局:浏览器开始计算渲染树中每个节点的位置和大小,这个过程被称为布局或重排。
绘制:浏览器遍历渲染树,使用UI后端层将每个节点绘制出来。
合成:最后,浏览器会根据绘制的顺序进行层的合成,然后显示在屏幕上。
以上就是浏览器的渲染过程,需要注意的是,这个过程是逐步完成的,为了更快的用户体验,渲染引擎将尽可能早的将内容呈现到屏幕上,并在后台继续处理剩余的工作。
vue路由模式
hash
模式是一种把前端路由的路径用井号 # 拼接在真实 url 后面的模式。当井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 onhashchange 事件。
https://juejin.cn/post/6993840419041706014
hash 通过 window.onhashchange 的方式,来监听 hash 的改变,借此实现无刷新跳转的功能。
hash 永远不会提交到 server 端(可以理解为只在前端自生自灭)。
history 通过 pushState 、 replaceState 来实现无刷新跳转的功能
使用 history 模式时,在对当前的页面进行刷新时,此时浏览器会重新发起请求。如果 nginx 没有匹配得到当前的 url ,就会出现 404 的页面。
而对于 hash 模式来说,它虽然看着是改变了 url ,但不会被包括在 http 请求中。所以,它算是被用来指导浏览器的动作,并不影响服务器端。
因此,改变 hash 并没有真正地改变 url ,所以页面路径还是之前的路径, nginx 也就不会拦截。
to B 的系统推荐用 hash ,相对简单且容易使用,且因为 hash 对 url 规范不敏感;
to C 的系统,可以考虑选择 H5 history ,但是需要服务端支持;
能先用简单的,就别用复杂的,要考虑成本和收益。
Set 和 Map有什么区别?
1、Map是键值对,Set是值得集合,当然键和值可以是任何得值
2、Map可以通过get方法获取值,而set不能因为它只有值
3、都能通过迭代器进行for...of 遍历
4、Set的值是唯一的可以做数组去重,而Map由于没有格式限制,可以做数据存储
Vuex有哪些基本属性?为什么 Vuex 的 mutation 中不能做异步操作?
有五种,分别是 State、 Getter、Mutation 、Action、 Module
state => 基本数据(数据源存放地)
getters => 从基本数据派生出来的数据
mutations => 提交更改数据的方法,同步
actions => 像一个装饰器,包裹mutations,使之可以异步。
modules => 模块化Vuex
Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
Loader和Plugin 有什么区别
Loader:直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader
。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。 Plugin:直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
UDP和TCP有什么区别
什么是媒体查询?
媒体查询可以让我们根据设备显示器的特性(如视口宽度、屏幕比例、设备方向:横向或纵向)为其设定CSS样式,媒体查询中可用于检测的媒体特性有 width 、 height 和 color (等)。使用媒体查询,可以在不改变页面内容的情况下,为特定的一些输出设备定制显示效果。
Vue的首屏加载优化方案
1、 使用CDN资源,减少打包体积,提高速度。
2、 路由懒加载
3、 把静态资源也放在CDN
4、 第三方组件库UI框架,使用按需引入(Tree Shaking)
5、 nginx开启gzip打包压缩
浏览器的性能监控你是怎么做的
- Lighthouse
Lighthouse
是 google 一个开源的自动化工具,运行 Lighthouse
的方式有两种:一种是作为 Chrome 扩展程序运行;另一种作为命令行工具运行。Chrome 扩展程序提供了一个对用户更友好的界面,方便读取报告。通过命令行工具可以将 Lighthouse 集成到持续集成系统。展示了白屏、首屏、可交互时间等性能指标和 SEO、PWA 等。
- PageSpeed
https://developers.google.com/speed/pagespeed/insights/
不仅展示了一些主要的性能指标数据,还给出了部分性能优化建议。
什么是diff算法
diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom。
简单来说Diff算法就是在虚拟DOM树从上至下进行同层比对,如果上层已经不同了,那么下面的DOM全部重新渲染。这样的好处是算法简单,减少比对次数,加快算法完成速度。
有两个特点:
比较只会在同层级进行, 不会跨层级比较
在diff比较的过程中,循环从两边向中间比较
防抖和节流
比如一个输入input 赋值给页面
防抖:一直输入,此时不赋值,等待停止输入一定的时间之后再赋值
节流:一直输入,每过一定的时间赋值一次
通信协议
在网络编程中,常用的协议有:
1、HTTP/HTTPS:这是最常用的网络协议,用于浏览器和服务器之间的通信。
2、FTP:文件传输协议,用于在网络上上传和下载文件。
3、TCP/IP:这是一种传输协议,用于在网络上发送数据。
4、WebSocket:这是一种实时通信协议,用于在浏览器和服务器之间进行全双工通信。
5、SMTP:简单邮件传输协议,用于发送电子邮件。
6、IMAP/POP3:这两种协议都用于接收电子邮件,但它们的工作方式略有不同。
以上就是一些常用的网络协议,具体使用哪种协议,取决于你的具体需求。
js的内存是怎么进行管理的
JavaScript 的内存管理主要由以下几个步骤组成:
1、分配内存:当你声明变量并赋值时,JavaScript 引擎会为变量分配内存。例如,当你写 let a = 1; 时,JavaScript 引擎会在内存中为变量 a 分配一个空间,用来存储值 1。
2、使用内存:当你使用变量时,就是在读取或写入内存。例如,当你写 let b = a; 时,JavaScript 引擎会读取变量 a 的内存,然后将读取到的值写入变量 b 的内存。
3、释放内存:当内存不再被需要时,JavaScript 引擎会释放内存。在 JavaScript 中,内存的释放主要通过垃圾回收(Garbage Collection)机制来完成。垃圾回收器会定期查找那些不再被引用的变量,然后释放它们的内存。
需要注意的是,JavaScript 的内存管理是自动进行的,开发者通常不需要(也无法)直接操作内存。但是,理解内存管理的原理,可以帮助我们写出更高效、更少出错的代码。
垃圾回收机制
垃圾回收(Garbage Collection,GC)是 JavaScript 内存管理的重要部分,它的主要任务是自动查找那些不再使用的内存并释放它们。
JavaScript 中的垃圾回收主要基于"可达性"的概念进行的。简单来说,"可达"的值是那些在内存中可以通过一系列引用关系访问到的值。例如,全局变量、局部变量等都是可达的。
垃圾回收器会定期执行,它会找出那些不再可达的对象,并释放它们的内存。这个过程是自动进行的,开发者无需(也无法)手动控制。
JavaScript 中的垃圾回收主要有两种算法:
1、标记-清除(Mark and Sweep):这是最常用的垃圾回收算法。它分为"标记"和"清除"两个阶段。在标记阶段,垃圾回收器会遍历所有的对象,对可达的对象进行标记。在清除阶段,垃圾回收器会清除那些未被标记(即不再可达)的对象。
2、引用计数(Reference Counting):这是一种较早的垃圾回收算法。它的主要思想是跟踪每个对象的引用数量。当一个对象的引用数量变为0时,就表示这个对象不再可达,垃圾回收器会立即释放它的内存。但这种算法有一个问题,就是无法处理循环引用的情况。
现代的 JavaScript 引擎通常会使用更复杂的垃圾回收算法,例如分代收集(Generational Collection)、增量收集(Incremental Collection)和空闲时间收集(Idle-time Collection)等。
哪些情况会导致内存泄漏
1、 意外的全局变量:由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收
2、 被遗忘的计时器或回调函数:设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
3、 脱离 DOM 的引用:获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
4、 闭包:不合理的使用闭包,从而导致某些变量一直被留在内存当中。
5、事件监听:如果添加了事件监听器,但是在不需要的时候没有移除,也可能导致内存泄漏。
6、数组或对象的大量数据:如果你的代码中有一个数组或对象存储了大量数据,但是在不需要这些数据的时候没有清空或删除,那么这些数据占用的内存就无法被回收,从而导致内存泄漏。
闭包一定会内存泄漏吗
不是的,闭包不一定会导致内存泄漏。只有当闭包不正确使用时,才可能导致内存泄漏。
闭包是 JavaScript 中一个重要的特性,它可以让函数记住并访问其所在的词法作用域,即使函数在其词法作用域之外执行。这种特性使得闭包在很多场景下都非常有用,例如创建私有变量、实现工厂函数、模块化等。
然而,如果闭包引用了外部作用域的变量,而这个变量又引用了一些大的对象或者DOM元素,那么即使这个大对象或DOM元素在其他地方已经不再需要,但由于闭包的引用,它们占用的内存无法被垃圾回收器回收,从而可能导致内存泄漏。
因此,使用闭包时需要注意:
只在必要时使用闭包,避免不必要的闭包创建。
在不再需要使用闭包内的大对象或DOM元素时,手动将它们设置为null,断开闭包对它们的引用,使得垃圾回收器可以回收它们的内存。
只要正确使用,闭包是不会导致内存泄漏的。
说一下data为什么是一个函数而不是一个对象?
JavaScript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。而在Vue中,我们更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义,这样当我们每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。
说一下for...in 和 for...of的区别?
1、for...of遍历获取的是对象的键值, for...in获取的是对象的键名;
2、for...in会遍历对象的整个原型链, 性能非常差不推荐使用,而for...of只遍历当前对象不会遍历原型链;
3、对于数组的遍历,for...in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for...of只返回数组的下标对应的属性值; 总结:for...in循环主要是为了遍历对象而生,不适用遍历数组; for....of循环可以用来遍历数组、类数组对象、字符串、Set、Map以及Generator对象
JSON.stringify有什么缺点
使用JSON.Stringify 转换的数据中,如果包含 function,undefined,Symbol,这几种类型,不可枚举属性, JSON.Stringify序列化后,这个键值对会消失。
转换的数据包含RegExp 引用类型序列化之后会变成空对象。
转换的数据中包含Date对象,JSON.Stringify序列化之后,会变成字符串。
转换的数据中包含 NaN,Infinity 值(含-Infinity),JSON序列化后的结果会是null。
无法序列化不可枚举属性。
无法序列化对象的循环引用,(例如: obj[key] = obj)。
无法序列化对象的原型链。
https://blog.csdn.net/weixin_44514665/article/details/115444273
AJAX中XMLHttpRequest有哪五大步骤
创建XMLhttpRequest对象
使用open方法设置和服务器的交互信息
如果默认是json格式,那就可以不设置requestHeader,默认的发送的是json格式数据
发送请求
如果请求完成,并响应完成,获取到响应数据(onreadystatechange)
vue3.0
1、 响应式原理的改变 Vue3.x 使用Proxy取代 Vue2.x 版本的Object.defineProperty
2、 组件选项声明方式Vue3.x 使用Composition API setup 是Vue3.x新增的一个选项,他是组件内使用Composition API 的入口
3、 模板语法变化slot具名插槽语法 自定义指令 v-model 升级
4、 其它方面的更改Suspense支持Fragment(多个根节点) 和Protal (在dom其他部分渲染组建内容)组件 针对一些特殊的场景做了处理。基于treeshaking优化,提供了更多的内置功能。
说一下SPA单页面有什么优缺点?
优点:
体验好,不刷新,减少 请求 数据ajax异步获取 页面流程;
前后端分离
减轻服务端压力
共用一套后端程序代码,适配多端
缺点:
首屏加载过慢;
SEO 不利于搜索引擎抓取
说一下proxy 它有什么优点
Object.defineProperty 拦截的是对象的属性,会改变原对象。proxy 是拦截整个对象,通过 new 生成一个新对象,不会改变原对象。
proxy 的拦截方式,除了上面的 get 和 set ,还有 11 种。选择的方式很多 Proxy,也可以监听一些 Object.defineProperty 监听不到的操作,比如监听数组,监听对象属性的新增,删除等。
说一下react和vue框架的区别
React与Vue的相同点
(1)都支持服务器渲染;
(2)都是数据驱动视图;
在以前,我们需要频繁操作DOM实现页面效果。而Vue和React就隐藏了DOM的频繁操作,采用数据驱动视图的方式,只需要关注数据的变化。
(3)都遵循组件化思想;
React和Vue都遵循组件化思想,它们把注意力放在UI层,将页面分成一一些细块,也就是组件,组件之间组合嵌套就形成最后的网页界面。
(4)都使用虚拟DOM;
(5)都有状态管理;
react有redux,vue有vuex。
不同点:
(1)框架本质不同;
Vue本质是MVVM框架,是由MVC发展来的;
React是前端组件框架,是由后端组件演化而来的。
(2)数据流不同;
Vue实现双向绑定,在vue1.0中有两种方法可以实现双向绑定,父子组件之间的props以及组件与DOM直接的v-model。vue2去掉了第一种双向绑定方法,通过v-model实现数据双向绑定。
React一直不支持双向绑定,提倡的是单向数据流(onChange/setState)。
(3)监听数据变化的实现原理不同;
Vue通过getter,setter以及一些函数的劫持,能精确知道数据的变化。
React是通过比较引用方式(diff)进行的,当应用的状态改变时,全部组件都会重新渲染。
(4)组件写法差异;
React推荐的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都写进 JavaScript 中;
Vue 推荐的做法是 template 的单文件组件格式,即 html,css,JS 写在同一个文件(vue也支持JSX写法)
(5)渲染过程不同。
Vue可以更快地计算出Virtual DOM的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
React在应用的状态被改变时,全部子组件都会重新渲染。通过shouldComponentUpdate这个生命周期方法可以进行控制。
(6)在state上的不同;
React中,state对象需要用setState方法更新状态;
在Vue中,state对象不是必须的,数据由data属性在vue对象中管理。
Vue作用域插槽
子组件:
父组件:
Var let const
为什么 Vue2 this 能够直接获取到 data 和 methods ?
通过this直接访问到methods里面的函数的原因是:
因为methods里的方法通过 bind 指定了this为 new Vue的实例(vm)。
通过 this 直接访问到 data 里面的数据的原因是:data里的属性最终会存储到new Vue的实例(vm)上的 _data对象中,访问 this.xxx,是访问Object.defineProperty代理后的 this._data.xxx。
Promise 如何解决回调地狱
首先,请你再回想一下什么是回调地狱,回调地狱有两个主要的问题:
多层嵌套的问题;
每种任务的处理结果存在两种可能性(成功或失败),那么需要在每种任务执行结束后分别处理这两种可能性。
这两种问题在“回调函数时代”尤为突出,Promise 的诞生就是为了解决这两个问题。Promise 利用了三大技术手段来解决回调地狱:回调函数延迟绑定、返回值穿透、错误冒泡。
说一说null 和 undefined 的区别,如何让一个属性变为null
null 是定义并赋值为null
undefined是定义未赋值
null = = undefined ==> ture null= = = undefined ==> false
如何实现可过期的localstorage数据?
一种是惰性删除:惰性删除是指获取数据的时候,拿到存储的时间和当前时间做对比,如果超过过期时间就清除Cookie。
另一种是定时删除:每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对CPU的长期占用。 LocalStorage清空应用场景:token存储在LocalStorage中,要清空
Axios拦截器的原理和应用
场景:请求拦截器用于在接口请求之前做的处理,比如为每个请求带上相应的参数(token,时间戳等)。 返回拦截器用于在接口返回之后做的处理,比如对返回的状态进行判断(token是否过期)。
拦截器原理:创建一个chn数组,数组中保存了拦截器相应方法以及dispatchRequest(dispatchRequest这个函数调用才会真正的开始下发请求),把请求拦截器的方法放到chn数组中dispatchRequest的前面,把响应拦截器的方法放到chn数组中dispatchRequest的后面,把请求拦截器和相应拦截器forEach将它们分unshift,push到chn数组中,为了保证它们的执行顺序,需要使用promise,以出队列的方式对chn数组中的方法挨个执行。 加分回答 Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。从浏览器中创建 XMLHttpRequests,从 node.js 创建 http 请求,支持 Promise API,可拦截请求和响应,可转换请求数据和响应数据,可取消请求,可自动转换 JSON 数据,客户端支持防御 XSRF
什么方法可以保持前后端实时通信?
轮训(占带宽,适合小型游戏),长轮训,websockect(实时通讯),sse,iframe
说一说服务端渲染
服务器端生成HTML直接返回给浏览器、减少网络传输、首屏渲染快、对搜索引擎友好
SSR是Server Side Render简称;页面上的内容是通过服务端渲染生成的,浏览器直接显示服务端返回的html就可以了。
使用场景:一般不会用在公司项目内(涉及前后端分离开发问题),可以用户博客网站、官网、营销类网站等比较注重加载速度和渲染效率的时候。
说说你对单向数据流和双向数据流的理解
单向数据流是指数据只能从父级向子级传递数据,子级不能改变父级向子级传递的数据。
双向数据流是指数据从父级向子级传递数据,子级可以通过一些手段改变父级向子级传递的数据。
比如用v-model、.sync来实现双向数据流。
什么是虚拟DOM?
虚拟DOM是将状态映射成视图的众多解决方案中的一种,其是通过状态生成一个虚拟节点树,然后使用虚拟节点树进行渲染生成真实DOM,在渲染之前,会使用新生成的虚拟节点树和上一次虚拟节点树进行对比,只渲染不同的部分。
Vue中如何实现一个虚拟DOM?说说你的思路
首先要构建一个VNode的类,DOM元素上的所有属性在VNode类实例化出来的对象上都存在对应的属性。例如tag表示一个元素节点的名称,text表示一个文本节点的文本,chlidren表示子节点等。将VNode类实例化出来的对象进行分类,例如注释节点、文本节点、元素节点、组件节点、函数式节点、克隆节点。
然后通过编译将模板转成渲染函数render,执行渲染函数render,在其中创建不同类型的VNode类,最后整合就可以得到一个虚拟DOM(vnode)。
最后通过patch将vnode和oldVnode进行比较后,生成真实DOM。
Vue实例挂载的过程是什么?
第一步:确保vm.$options有render函数。
第二步: 在原始的$mount方法,先触发beforeMount钩子函数,然后创建一个Watcher实例,在第二参数传入一个函数vm._update。
Vue为什么要求组件模板只能有一个根元素
当前的virtualDOM差异和diff算法在很大程度上依赖于每个子组件总是只有一个根元素。
如果想扩展某个现有的Vue组件时,怎么做呢
用mixins混入
用extends,比mixins先触发
用高阶组件HOC封装
vue中mixins和extends有什么区别
1、mixins接收对象数组(可理解为多继承),extends接收的是对象或函数(可理解为单继承)
2、优先级>extends>mixins,继承钩子函数的时候,是不进行覆盖的,extends的钩子函数先触发,而后再是mixins的钩子函数触发,最后就是组件自身的钩子函数触发。
3、mixins类似于面向切面的编程(AOP),extends类似于面向对象的编程
vue-loader是什么?它有什么作用?
vue-loader是一个webpack的loader,是一个模块转换器,用于把模块原内容按照需求转换成新内容。
它允许你以一种名为单文件组件 (SFCs)的格式撰写 Vue 组件。可以解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的loader去处理。
数据结构-栈的特性
先进后出、后进先出
栈帧永远指向的是顶部的数据结构
处于栈顶的原数具备活跃权
instanceOf原理
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left); // 获取对象的原型
while (true) {
if (proto === null) return false; // 找到了 Object.prototype.__proto__ 为 null,说明已经找到了顶层
if (proto === right.prototype) return ture; // 在某一层原型链上找到了
proto = Object.getPrototypeOf(proto); // 继续向上查找
}
}
// 测试
console.log(myInstanceof(new Number(123), Number)); // true
console.log(myInstanceof(123, Number)); // false
function Person() {}
let p = new Person();
console.log(p.__proto__ === Person.prototype); // true
console.log(Person.__proto__ === Function.prototype); // true
wangeditor
WangEditor是一款基于JavaScript的轻量级web富文本编辑器,其内核主要是基于浏览器提供的document.execCommand API进行封装和扩展实现的。这个API提供了一种用于处理文档或编辑区域中的当前选定内容的方法。
WangEditor通过这个API实现了文本的各种编辑功能,如加粗、斜体、下划线、插入图片、创建链接等等。同时,WangEditor还提供了丰富的API和配置项,使得开发者可以根据自己的需求进行定制和扩展。
IndexedDB
IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索。虽然 Web Storage 在存储较少量的数据很有用,但对于存储更大量的结构化数据来说力不从心。而 IndexedDB 提供了这种场景的解决方案。
WebSocket
WebSocket 通信过程主要包括以下几个步骤:
1、建立连接:首先,客户端发起一个特殊的 HTTP 请求,这个请求被称为 WebSocket 握手请求。这个请求看起来像普通的 HTTP 请求,但有几个特殊的头部字段,例如 Upgrade: websocket 和 Connection: Upgrade。当服务器收到这个请求后,如果同意建立 WebSocket 连接,就会返回一个特殊的 HTTP 响应,这个响应的头部字段包括 Upgrade: websocket 和 Connection: Upgrade。这样,WebSocket 连接就建立成功了。
2、数据传输:一旦 WebSocket 连接建立成功,客户端和服务器就可以通过这个连接进行全双工通信,即客户端可以向服务器发送数据,服务器也可以向客户端发送数据。这种通信方式与 HTTP 不同,HTTP 是单向的,只能由客户端向服务器发送请求,然后由服务器返回响应。
3、关闭连接:任何一方都可以发起关闭 WebSocket 连接的请求。当一方发起关闭连接的请求后,另一方可以返回一个确认关闭的消息,然后双方都关闭连接。也可以由一方直接关闭连接,另一方在检测到连接关闭后,也关闭连接。
以上就是 WebSocket 的通信过程。需要注意的是,WebSocket 是一种持久连接,一旦建立,就会一直保持连接,直到一方关闭连接。这使得 WebSocket 非常适合需要实时通信的场景,例如聊天应用、在线游戏等。
以下是一个简单的 WebSocket 客户端示例代码:
javascript
// 创建一个 WebSocket 连接
var socket = new WebSocket("ws://localhost:8080");
// 连接打开时触发
socket.onopen = function(event) {
console.log("WebSocket 连接已打开");
// 发送一个消息
socket.send("Hello, Server!");
};
// 接收到消息时触发
socket.onmessage = function(event) {
console.log("收到消息: " + event.data);
};
// 连接关闭时触发
socket.onclose = function(event) {
console.log("WebSocket 连接已关闭");
};
// 连接发生错误时触发
socket.onerror = function(error) {
console.log("WebSocket 错误: " + error);
};
// 这个示例代码创建了一个 WebSocket 连接,然后定义了几个事件处理函数,分别处理连接打开、接收到消息、连接关闭和发生错误等事件。
// 需要注意的是,这个示例代码只是一个基本的示例,实际使用时可能需要处理更多的情况,例如重连、心跳检测等。
postMessage
postMessage
是 HTML5 引入的一种新的通信方式,主要用于解决以下两类问题:
跨域通信:由于同源策略的限制,不同源的窗口(或者 iframe)之间无法直接通信。
postMessage
提供了一种可以在任何源之间进行安全通信的方法。你可以使用postMessage
向另一个窗口发送消息,无论这个窗口是否同源。接收窗口可以监听message
事件来接收消息。多窗口通信:在一个浏览器中,可能会打开多个窗口或标签页。这些窗口或标签页之间可以通过
postMessage
进行通信。例如,你可以在一个窗口中打开另一个窗口,然后使用postMessage
向新打开的窗口发送消息。
以下是一个 postMessage
的简单示例:
// 在窗口 A 中
var winB = window.open('http://www.example.com');
winB.postMessage('Hello, Window B!', 'http://www.example.com');
// 在窗口 B 中
window.addEventListener('message', function(event) {
if (event.origin !== 'http://www.example.com') return;
console.log('收到消息:' + event.data);
});
在这个示例中,窗口 A 打开了窗口 B,然后向窗口 B 发送了一个消息。窗口 B 通过监听 message
事件来接收消息。注意,为了安全起见,接收消息时应该检查 event.origin
是否是预期的源。
跨域问题
1、CORS(跨源资源共享):这是最常用的解决跨域问题的方法。服务器设置相应的 CORS 头部字段,如 Access-Control-Allow-Origin,允许来自不同源的请求。
2、JSONP(JSON with Padding):这是一种早期的解决跨域问题的方法,它利用了 <script> 标签没有跨域限制的特性。但 JSONP 只能发送 GET 请求,且安全性较低。
3、代理服务器:可以设置一个代理服务器来转发请求,由于请求是从同源的代理服务器发出的,所以不会触发跨域问题。
4、WebSockets:WebSockets 不受同源策略的限制,可以用来进行跨域通信。
5、postMessage:HTML5 引入的一种新的通信方式,可以实现跨源通信。
6、document.domain:如果两个页面的二级域名相同,那么可以通过设置 document.domain 来共享 cookie。
7、window.name、location.hash:这两种方式也可以实现跨域,但使用较少。
以上就是一些解决跨域问题的常见方法,具体使用哪种方法,取决于你的具体需求和环境。
服务和服务之间有没有跨域
服务与服务之间的通信,通常不受浏览器的同源策略限制,因此不存在跨域问题。同源策略是浏览器为了安全性,阻止不同源之间的 JavaScript 代码进行交互而设置的限制。
在服务端,可以自由地向任何服务器发送请求,不受同源策略的限制。例如,一个运行在 A 服务器上的应用可以向 B 服务器发送请求,获取数据或者调用 API,这不会触发跨域问题。
然而,虽然服务端不受同源策略的限制,但在进行服务间通信时,仍然需要考虑安全性和权限控制。例如,可能需要使用 API 密钥、OAuth、签名等方式来验证请求的合法性。
前端安全方面有哪些攻击方式
前端安全主要需要防范以下几种常见的攻击方式:
XSS(跨站脚本攻击):攻击者通过在网页中注入恶意脚本,当其他用户浏览这个网页时,恶意脚本会被执行,从而达到攻击的目的。防范方法主要是对用户输入进行合适的过滤或转义。
CSRF(跨站请求伪造):攻击者诱导用户去点击一个链接或者提交一个表单,这个链接或表单中包含了攻击者预设的请求,从而在用户不知情的情况下以用户的身份发送请求。防范方法主要是使用 CSRF Token,或者进行 SameSite Cookie 设置。
点击劫持:攻击者通过覆盖一个透明的层或者使用其他方式诱导用户点击,用户以为是在进行正常操作,实际上是在进行攻击者预设的操作。防范方法主要是使用 X-FRAME-OPTIONS 响应头来防止网页被嵌入到其他网页中。
中间人攻击:攻击者拦截并修改通信双方的通信内容。防范方法主要是使用 HTTPS 来加密通信内容。
钓鱼攻击:攻击者通过伪造网站或者电子邮件,诱导用户输入敏感信息,如用户名、密码、信用卡号等。防范方法主要是提高用户的安全意识,不轻易相信未知或者不安全的链接。
以上就是一些常见的前端安全攻击方式,防范这些攻击需要前端、后端和用户共同配合,提高系统的安全性。
node有哪些框架可以处理脚本攻击
在 Node.js 中,有一些流行的框架和库可以帮助我们处理脚本攻击,如 XSS 和 CSRF 等:
Express.js:Express 是一个流行的 Node.js web 应用框架,它有很多中间件可以帮助处理脚本攻击。例如,
csurf
中间件可以帮助防止 CSRF 攻击,helmet
中间件可以帮助设置各种 HTTP 头以增强应用的安全性。Koa.js:Koa 是另一个流行的 Node.js web 应用框架,它的设计更加现代和灵活。Koa 也有很多中间件可以帮助处理脚本攻击,例如
koa-helmet
、koa-csrf
等。csurf:这是一个 Node.js 的 CSRF 防护中间件,可以用在 Express、Koa 等框架中。
helmet:这是一个 Node.js 的安全头部设置中间件,可以帮助防止各种脚本攻击,如 XSS、点击劫持等。
xss-filters:这是一个 Node.js 的 XSS 防护库,提供了一系列的 API 来对用户输入进行过滤和转义。
lusca:这是 Express 应用的安全中间件,由 Paypal 开发,可以防止 CSRF 和 XSS 攻击。
以上就是一些在 Node.js 中处理脚本攻击的框架和库,使用它们可以帮助我们更好地保护我们的应用不受脚本攻击。
为什么需要3次握手,2次或4次行不行?
TCP 三次握手是为了在客户端和服务器之间建立可靠的连接,并且能够防止已经失效的连接请求报文段突然又传到了服务端,因而产生错误。具体来说,主要有以下两个原因:
防止已失效的连接请求报文段被服务器接收:由于网络原因,有可能客户端发送的连接请求报文段在网络中滞留,然后延迟到达服务器,如果不进行三次握手,服务器接收到这个已经失效的连接请求报文段后,就会错误地打开连接,浪费资源。
确保双方都准备好接收和发送数据:三次握手可以确保双方都已经准备好接收和发送数据。在第一次握手,客户端发送连接请求报文段;第二次握手,服务器接收到连接请求后,回复 ACK 包,并发送自己的连接请求报文段;第三次握手,客户端接收到服务器的连接请求后,再次发送 ACK 包。这样,双方都确认了对方已经准备好接收和发送数据。
如果只进行两次握手,无法解决上述问题;如果进行四次或更多次握手,虽然理论上可行,但会增加网络的开销,而且没有必要,因为三次握手已经能够满足需求。因此,TCP 选择了三次握手来建立连接。
tcp粘包
TCP粘包是指发送方发送的若干包数据到达接收方时,被TCP接收后,就以连续的流形式放入接收缓冲区中。接收方应用程序在其缓冲区中一次性读取了多个包数据。造成粘包的原因有以下几点:
- 发送方的数据发送太快,接收方来不及接收,造成接收端缓冲区满,这样后来的包就粘在了一起。
- 运行在接收方的应用程序不能及时的处理接收缓冲区的包,造成多个包接收在一起。
- 还有一种可能就是由TCP的Nagle算法造成的。该算法的目的是为了提高宽带利用率。
解决TCP粘包的方法主要有以下几种:
- 固定长度的包头,包头中使用一个字段标识数据的长度,接收方通过这个长度读取整个包。
- 在包尾部增加结束符,接收方通过结束符来确定一个包已经接收完毕。
- 将消息分为消息头和消息体,消息头中包含表示数据长度的字段(类似于第一种方法)。
以上就是关于TCP粘包的一些基本概念和解决方法。
在前端,我们通常不直接处理TCP粘包的问题,因为TCP协议的处理通常是在操作系统层面,而前端开发主要关注的是应用层面。在应用层面,我们使用的是HTTP、WebSocket等基于TCP的协议,这些协议已经帮我们处理了TCP粘包的问题。
如果你在特殊的场景下需要处理TCP粘包,可能需要使用一些底层的网络编程接口,这通常不是前端开发的范畴。如果你在使用Node.js进行服务器端开发,那么可以使用net模块来进行TCP通信,处理粘包问题。
在处理TCP粘包时,一般的策略是:
- 设计一个固定长度的包头,包头中包含了整个数据包的长度信息。当接收到数据时,先读取包头,获取数据包的长度,然后再根据这个长度读取剩余的数据。这样就可以确保每次都能正确地读取到完整的数据包。
- 另一种方法是在每个数据包的末尾添加一个特殊的结束符,接收方通过这个结束符来判断一个数据包是否接收完毕。这种方法的缺点是需要确保数据中不会出现这个结束符,否则会导致数据包的边界判断错误。
以上就是在前端处理TCP粘包的一些基本思路,但需要注意的是,这通常不是前端开发需要关注的问题,除非你在进行一些更底层的网络编程。
进程、线程区别
进程和线程都是操作系统进行任务调度的基本单位,但它们之间有一些关键的区别。
进程是操作系统资源分配的最小单位,它包含了运行程序所需要的资源,如CPU时间、内存空间、文件、设备等。每个进程都有自己独立的内存空间,进程之间的通信需要通过进程间通信(IPC)机制进行。
线程是操作系统任务调度的最小单位,它是在进程内部进行的并发执行的单元。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件、设备等。因此,线程之间的通信比进程之间的通信要简单得多。
举个例子,可以把进程想象成一个工厂,工厂内有很多资源,如原材料、机器、电力等。而线程就像是工厂里的工人,他们共享工厂的资源,同时各自进行不同的任务。如果一个工人(线程)出了问题,只会影响他自己的工作,不会影响其他工人。但如果工厂(进程)出了问题,那么所有的工人都会受到影响。
浏览器的核心线程和核心进程有哪些
浏览器的核心进程主要包括:
浏览器进程:负责浏览器界面显示,用户交互,子进程管理,同时提供存储等功能。
渲染进程:负责将HTML、CSS和JavaScript转变为用户可以与之交互的网页,渲染引擎在这个进程中。
插件进程:负责控制网页中的插件执行,因为插件容易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会影响浏览器和网页的工作。
GPU进程:负责处理GPU任务,其实每个标签页都有自己的GPU进程。
浏览器的核心线程主要包括:
GUI渲染线程:负责渲染浏览器界面,解析HTML、CSS、构建DOM树等。
JavaScript引擎线程:负责处理JavaScript脚本,执行代码。
定时触发器线程:处理定时器和计数器。
事件触发线程:负责将准备好的事件交给JavaScript引擎执行。
异步http请求线程:处理异步请求和响应。
以上就是浏览器的核心进程和线程,它们共同协作,使得浏览器能够正常工作。
多线程
多线程是指在一个程序中包含多个线程,这些线程可以并发执行,提高了程序的执行效率。
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程中可以同时运行多个线程,这就是多线程。
多线程的主要优点有:
提高程序的响应速度。如果一个线程需要等待(比如等待用户输入或者等待硬盘读取数据),那么在单线程环境下,整个程序就会被阻塞。而在多线程环境下,其他线程还可以继续执行,提高了程序的响应速度。
提高计算机资源的利用率。在多核CPU环境下,多线程可以使得每个核心都有任务执行,提高了CPU的利用率。
简化程序结构。在处理复杂问题时,多线程可以简化程序的结构,使得程序更易于理解和编写。
但是,多线程也有一些缺点,主要包括:
线程切换的开销。操作系统需要在不同的线程之间进行切换,这会带来一定的开销。
线程同步问题。多个线程可能会共享一些资源,如果没有正确地处理,可能会导致数据的不一致。
线程安全问题。多线程环境下,需要注意线程安全问题,否则可能会出现数据混乱等问题。
在编程时,可以通过创建线程、结束线程、线程同步(如互斥锁、信号量等)等操作来进行多线程编程。不同的编程语言提供了不同的多线程编程接口,如Java、C++、Python等都支持多线程编程。
post为什么会发送两次请求
1、什么场景下会在一次请求里会发起两次请求;
2、第一次和第二次请求分别是什么,有什么区别?
3、为什么浏览器有同源策略,而服务器没有?
4、服务器如何配置cors。如果是nginx里如何配置,如果是nodejs后端,例如koa或者egg如何配置;
5、为什么本地使用webpack进行dev开发时,访问的是127.0.0.1,但是却依然能在不需要服务器端配置cors的情况下访问到线上接口?
在一次请求中发起两次请求的场景通常出现在跨域请求中,这种情况下的第一次请求被称为预检请求(Preflight Request)。预检请求是浏览器自动发起的,用于确认真实请求是否安全可行。
第一次请求(预检请求)是一种OPTIONS请求,它用于询问服务器,针对目标资源所设定的CORS策略。第二次请求是真正的业务请求,它会在预检请求通过后发起。两者的区别在于,第一次请求不携带用户数据,只用于确认接下来的真实请求是否被允许;第二次请求才是真正的业务请求,携带用户数据,并获取服务器响应。
浏览器有同源策略是为了保护用户信息的安全,防止恶意的网站通过脚本进行跨站请求伪造。而服务器没有同源策略,是因为服务器不直接面向用户,不存在用户信息被盗取的风险。
在Nginx中配置CORS,可以在server或location块中添加如下配置:
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
在Node.js的Koa框架中,可以使用koa2-cors中间件来配置CORS:
const Koa = require('koa');
const cors = require('koa2-cors');
const app = new Koa();
app.use(cors({
origin: '*',
allowMethods: ['GET', 'POST', 'OPTIONS'],
}));
在Egg框架中,可以在config.default.js中配置:
config.security = {
domainWhiteList: ['*'],
methodnoallow: { enable: false },
csrf: { enable: false },
};
- 在使用Webpack进行本地开发时,可以通过配置代理(proxy)来解决跨域问题。Webpack dev server会创建一个开发服务器,并且将所有的API请求代理到指定的服务器地址,因此不需要服务器端配置CORS。