1996年 1999年 2015年
第一种方式:jsonp请求;jsonp的原理是利用
短链接
概念:客户端与服务器建立连接开始通信,一次/指定次数通信结束之后就断开本次TCP连接, 当下次再次通信时,再次建立TCP的链接。
优点:不长期占用服务器的内存,那么服务器能处理的连接数量是比较多的
缺点:1、因为要等到发送或者获取资源时,才去请求建立连接,而且http协议只能客户端主动向服务端发送数据后,服务端才返回 对应的数据,那么服务端想主动发送数据给客户端呢?Websocket可以让服务端主动发送数据给客户端,或者要等到下一次要请求数据时,才发送,比如我们采用 轮询(30秒或者更长)拉取消息, 那么服务器与客户端通信的实时性就丧失了。
2、客户端采用轮询来实时获取信息,或者说大量的客户端使用短连接的方式通信,那么就浪费了大量的CPU和带宽资源用于建立连接 和释放连接,存在资源浪费,甚至是无法建立连接。比如经典的http长轮询(微信网页客户端端)
长连接
概念: TCP与服务器建立连接之后一直处于连接状态,直到最后不再需要服务的时候才断开连接
优点: 1、传输数据快 2、服务器能够主动第一时间传输数据到客户端
缺点: 1、因为客户端与服务器一直保持这种连接,那么在高并发分布式集群系统中客户端数量会越来越多,占 用很多的系统资源 2、TCP本身是一种有状态的数据,在高并发分布式系统会导致后台设计比较难做。
把IP想像成一种高速公路,它允许其它协议在上面行驶并找到到其它电脑的出口。TCP和UDP是高速公路上的“卡车”,它们携带的货物就是像HTTP,文件传输协议FTP这样的协议等。
TCP和UDP的区别
什么是HTTP?
HTTP是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wLnnq3HY-1609201054581)(…/…/Pictures/Camera%20Roll/1418466-20180810112625596-2103906128.png)]
缓存 的好处:
1.减少服务器压力
2.缩短页面响应时间
3.减少冗余的数据传输,减少了网络费用
强制缓存 :在第一次访问取到数据后,以后在过期时间内不会再请求
http1.0 的expires 设定过期时间
http 1.1 的cache-control max-age
协商缓存 :第一次请求服务器后,服务器会将缓存的标识和数据一起发送给客户端,客户端将二者备份至缓存数据库中,再次请求时服务器时进行判断,如果成功,将返回304不会返回任何数据,通知客户端可以使用缓存数据。
Last-Modified / If-Modified-Since
服务器修改的最后时间,如果时间大于If-Modified-Since > 最后修改时间 则返回200 否则返回304
1、http缓存
是基于HTTP协议的浏览器文件级缓存机制。
2、websql
这种方式只有较新的chrome浏览器支持,并以一个独立规范形式出现
3、indexDB
是一个为了能够在客户端存储可观数量的结构化数据,并且在这些数据上使用索引进行高性能检索的 API
4、Cookie
一般网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)
5、Localstorage
html5的一种新的本地缓存方案,目前用的比较多,一般用来存储ajax返回的数据,加快下次页面打开时的渲染速度
6、Sessionstorage
和localstorage类似,但是浏览器关闭则会全部删除,api和localstorage相同,实际项目中使用较少。
7、application cache
是将大部分图片资源、js、css等静态资源放在manifest文件配置中
8、cacheStorage
是在ServiceWorker的规范中定义的,可以保存每个serverWorker申明的cache对象
9、flash缓存
这种方式基本不用,这一方法主要基于flash有读写浏览器端本地目录的功能
物理层-数据链路层-网络层-传输层-会话层-表示层-应用层
发送一个http,将返回来的http里的数据进行解析,
SYN这个标志位只有在TCP建立连接时才会被置1 ,握手完成后SYN标志位被置0
为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送 后的情况置之不理,它一定会向对方确认是否成功送达。握手过程中使用了TCP的标志:SYN和ACK。
发送端首先发送一个带SYN标志的数据包给对方。接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息。 最后,发送端再回传一个带ACK标志的数据包,代表“握手”结束。 若在握手过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包。
断开一个TCP连接则需要“四次握手”:
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
为什么断开连接需要四次?因为服务端接受到报文之后不会立即断开连接
sql注入原理
就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
总的来说有以下几点:
1.永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。
2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4.不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。
XSS原理及防范
Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者javascript代码。比如:攻击者在论坛中放一个
看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,
当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
XSS防范方法
首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。
首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。
其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。
如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。
尽量采用POST 而非GET 提交表单
XSS与CSRF有什么区别吗?
XSS是获取信息,不需要提前知道其他用户页面的代码和数据包。CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。
要完成一次CSRF攻击,受害者必须依次完成两个步骤:
登录受信任网站A,并在本地生成Cookie。
在不登出A的情况下,访问危险网站B。
CSRF的防御
服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。
通过验证码的方法
跨域的产生是由于浏览器的同源策略,同源策略指的是: 1、域名 2、端口号 3、协议名 只要有一个不同就称为跨域 解决跨域:
1、jsonp跨域(只能解决get) 原理:动态创建一个script标签。利用script标签的src属性不受同源策略限制,因为所有的src属性和href属性都不受同源策略的限制,可以请求第三方服务器资源内容
2、服务器设置对CORS的支持 原理:服务器设置Access-Control-Allow-Origin HTTP响应头之后,浏览器将会允许跨域请求
Webstorage(localStorage sessionStorage) cookie
1、最大的特点就是服务端可以主动向客户端发送数据
2、与 HTTP 协议有着良好的兼容性。默认端口是80和443,并握手阶段采用HTTP协议,因此握手时不容易屏蔽,能通过各种HTTP代理服务器。
3、数据格式比较轻量,性能开销小,通信高效。
4、可以发送文本,也可以发送二进制数据。
5、没有同源限制,客户端可以与任意服务器通信。
6、协议标识符是ws(如果加密,则为wss,加密层是TLS),服务器网址就是 URL。
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。
1 hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.abc.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
2 history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
3 结合自身例子,对于一般的 Vue + Vue-Router + Webpack + XXX 形式的 Web 开发场景,用 history 模式即可,只需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。
代码层面:避免使用css表达式,避免使用高级选择器,通配选择器。
缓存利用:缓存Ajax,使用CDN,使用外部js和css文件以便缓存,添加Expires头,服务端配置Etag,减少DNS查找等
请求数量:合并样式和脚本,使用css图片精灵,初始首屏之外的图片资源按需加载,静态资源延迟加载。
请求带宽:压缩文件,开启GZIP,
代码层面的优化
用hash-table来优化查找
少用全局变量
用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能
用setTimeout来避免页面失去响应
缓存DOM节点查找的结果
避免使用CSS Expression
避免全局查询
避免使用with(with会创建自己的作用域,会增加作用域链长度)
多个变量声明合并
避免图片和iFrame等的空Src。空Src会重新加载当前页面,影响速度和效率
尽量避免写在HTML标签中写Style属性
优点:
缺点:
1.将js放在最后加载
2.监听onload 异步 加载
线程在进程下行进(单纯的车厢无法运行)
一个进程可以包含多个线程(一辆火车可以有多个车厢)
不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
1.IE Trident内核
2.firefox Gecko内核 (该扣)
3.谷歌 blink
4.safari webkit
为什么会出现盒子塌陷?
在CSS出现之前使用的P标签,是设计师要求的,是为了兼容css1.0之前的P标签,他要求每个段落都有相同间隔,所以当你对P标签设置了margin-top和margin-bottom时候就发现两个margin合并的情况,并且取最大值。
直译为块级格式化上下文,数于同一个BFC的两个相邻Box会发生重叠,BFC里的元素与外面的元素不会发生影响 。
如何创建BFC?触发BFC的四个条件,满足其中一个即可
边距重叠什么时候发生?
行内> !important > id选择器 > 属性选择器 = 类选择器 = 伪元素选择器 > 标签选择器 = 伪类选择器(::after)
flex: 1 === flex: 1 1 0%; flex-grown flex-shrink flex-basis
1.创建空对象
2.将构造函数中的作用域赋给新对象(因此this就指向了新对象)
3.执行构造函数中的代码(为新对象添加属性)
4.返回新对象
let liu = new Person('liu','18')
let a = {}
a.__proto__ = Person.prototype
Person.call(a)
promise原型上的方法有then,catch 构造函数上有all,race,reject,resolve
Promise的三种状态,catch和reject的区别?
catch是原型上的属性,reject是promise属性
catch会捕获promise链的所有异常
reject返回一个错误的reject
slice splict fangfa map fulter gaibianshuzu fanhuizhi
谈一谈你对原型链的理解
每一个构造函数都有prototype属性,
apply 第一个参数是this指向,第二个参数是函数接受的参数,立即执行
call 第一个参数是this指向,后面传入的是一个参数列表 立即执行
bind 第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。
async await:async 可以让函数具有异步特征,但是仍是同步执行,当有返回值时,返回的是一个promise.resolve包装的期约对象,,当遇到await的时候,会等待后面的promise执行完,然后执行后面代码。
promise:Promise本身是同步的,但在执行resolve或者rejects时是异步的,即then方法是异步的。
setTimeout:异步执行函数是宏任务
common JS module.exports require()
ES6 模块不共享全局命名空间。
模块顶级 this 的值是 undefined(常规脚本中是 window)。
ES6 模块默认在严格模式下执行。
ES6 模块是异步加载和执行的。
require是值的拷贝,import是值的引用
ES可以先使用再引用,requrie必须先引用
require 可以放到模块中去 module.exports 的会被缓存 ES6的不会被缓存
ES6 最大的一个改进就是引入了模块规范 A 依赖B ,B依赖C,C依赖D, 他会先导入A发现需要B再导入B以此类推
export:export 语句与导出值的相对位置或者
export 关键字在模块中出现的顺序没有限制。export 语句甚至可以出现在它要导出的值之前
// 允许
const foo = 'foo';
export { foo };
// 允许
export const foo = 'foo';
// 允许,但应该避免
export { foo };
const foo = 'foo';
export default只能有一个,重复的话会报错。 导入的时候不需要加大括号
import 和export一样,必须出现在模块的顶级(不是顶部,是顶级)导入的时候需要加大括号
原型的作用:①数据共享,节约资源。②继承
原型链的定义:每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数的原型。这样就在实例和原型之间构造了一条原型链。
特例:
Object.__proto__ === Function.prototype //true
Object.__proto__ === Function.__proto__//true
Object.prototype === Function.prototype.__proto__ // true
//因此
Function instanceof Object //true
Object instanceof Function //true
const arr = [1,2,3,4]
会改变原来数组的:
pop()—删除数组的最后一个元素并返回删除的元素。
push()—向数组的末尾添加一个或更多元素,并返回新的长度。
shift()—删除并返回数组的第一个元素。
unshift()—向数组的开头添加一个或更多元素,并返回新的长度。
reverse()—反转数组的元素顺序。
sort()—对数组的元素进行排序。
splice()—用于插入、删除或替换数组的元素。 返回的是含有被删除的数组
································································································
不会改变原来数组的:
concat()—连接两个或更多的数组,并返回结果。
every()—检测数组元素的每个元素是否都符合条件。
some()—检测数组元素中是否有元素符合指定条件。
filter()—检测数组元素,并返回符合条件所有元素的数组。
indexOf()—搜索数组中的元素,并返回它所在的位置。
join()—把数组的所有元素放入一个字符串。
toString()—把数组转换为字符串,并返回结果。
lastIndexOf()—返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
map()—通过指定函数处理数组的每个元素,并返回处理后的数组。
slice()—选取数组的的一部分,并返回一个新数组。
valueOf()—返回数组对象的原始值。
快速排序 基准值放中间 小的放左边 大的放右边 当有最不稳定
冒泡排序 i = arr.length -1 如果arr[j]小则进行交换 把最大值放在后面
选择排序 把最小值放在前面
手机号正则表达式
/^1[3|4|5|7|8][0-9]{9}$/
邮箱正则表达式
/^[A-z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
function mySetInterval(fn, time) {
function interval(){
setTimeout(interval, time)
fn()
}
setTimeout(interval, time)
}
检测到touched事件的时候,会通过DOM自定义事件发送一个Click事件,并将浏览器在300ms后的Click事件给阻止掉
缺点:脚本相对较大。
事件循环,js是单线程,所有的异步操作都是模拟出来的,所有任务可分为同步任务与异步任务,首先执行宏任务script整体代码,首先判定是同步任务还是异步任务如果是同步任务则进入主线程立即执行,如果是异步任务则进入任务队列,当主线程任务执行完就会将任务队列里的任务推入主线程中。异步任务被分为宏任务与微任务,当js执行到宏任务的时候会将他们加入到宏任务事件队列里面,执 行到微任务的时候会将他们推入到 当主线程,微任务比如说promise等,宏任务有setTimeout,setInterval等等。
当遇到同步任务则直接执行,如果遇到微任务则推入微任务队列,当遇到宏任务则推入宏任务队列,当整个宏任务执行完,则会执行所有微任务,当微任务执行完,则会查看是否有宏任务重复上述操作
如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。声明之前的区域就形成了死区。
obj.clientWidth //获取元素的宽度(width+padding)
obj.clientHeight //元素的高度
obj.offsetLeft //元素相对于父元素的left 是否有父元素有的话 .offsetParent 重复操作
addEventListener 新版本
attachEvent 老版本 事件类型前加on
/*事件委托(事件代理): 即是把原先绑定子元素的事件(click,ablClick,keydown,…)委托给父元素,让父元素担当监听职务, 事件代理的原理是Dom元素的事件冒泡。
//1.事件委托:父元素ul var ul=document.getElementsByTagName(“ul”)[0]; ul.addEventListener(“click”,fn1,false);//DOM2级事件添加 单击,函数执行,冒泡; function fn1(e){ //函数执行 var e=e||window.event; //老式IE的兼容语句 var tg=e.target; switch (tg.id) { //通过id控制 case “first”:alert(“第一个li被单击了”); break; case “two”:alert(“第二个li被单击了”); break; case “third”:alert(“第三个li被单击了”); break; case “fo”:alert(“第四个li被单击了”); break; } }
语法简洁,更加语义化,业务逻辑更清晰
基于标准 Promise 实现,支持 async/await
fetch(url).then(response => response.json())``//解析为可读数据
.then(data => console.log(data))``//执行结果是 resolve就调用then方法
.catch(err => console.log(``"Oh, error"``, err))``//执行结果是 reject就调用catch方法
vue体积小,易用于小型项目,有双向绑定,不用直接操作DOM,容易上手,国人开发在中国使用率更高
区别:react更适用于大型企业,没有双向绑定,谷歌公司开发,生态圈更大,react是JSX书写,vue使用HTML模板书写。
vue通过Object.defineProperty的set和get进行数据劫持和结合发布者订阅者模式进行更改DOM。
首先vue有一个监听者obserber用于监听数据的变化,当数据发生变化时,通过Object.defineProperty的set和get拦截数据,传给订阅者,由于订阅者过多所以有个监听器用于管理和通知订阅者的,解析器compile用于解析每个元素节点的指令和解析,根据指令模板替换数据,以及绑定相应的更新函数,订阅者用于收集每个属性的变化并将指令绑定相应的函数,从而更新视图。
首先数据存放在vue的data中,然后利用递归将observer用Object.keys和forEach将data中的每一个属性遍历出来,并利用Object.defineProperty的set和get属性监听data中的每一个数据变化。然后有个dep函数,里面有subs订阅器数组用来收集订阅者的,dep原型里面有两个函数对象,一个addSub函数对象用于subs数组添加订阅者,还有另一个notify用于遍历每个订阅者并将每个订阅者绑定update方法。
通知所有订阅者,数值一旦变化,notify被调用,然后通知每一个监听者,subs里面有好多watcher,执行update,update调用观察者原型的run方法,run调用get方法
观察者模式是一对多的一种模式,一:你改了某一个data数据,多:页面上凡是用了这个数据的地方,都更新 ,因此多地方都观察这个data
observer:遍历data中的所有属性并在每个方法上添加Object.defineProperty的set和get方法进行数据劫持。
第一步监听每个数据变化 compile解析html中指令替换成数据,初始化视图。创建watcher,绑定更新函数,并会触发在Object.defineProperty里的get方法里面的addSub方法,该方法会将watcher添加到订阅器dep中
第二步告诉watcher订阅者触发更新函数 update
第三步watcher订阅者的由来
观察者 :利用Obeject.defineProperty()
来监听属性变动 那么将需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter
和getter
这样的话,给这个对象的某个值赋值,就会触发setter
,那么就能监听到了数据变化。
订阅器 :用于监听Data的改变,当Data发生改变的时候,会遍历所有订阅者并添加update方法,
解析器 :将模板中的变量替换成数据,初始化渲染页面视图,并将每个指令节点绑定更新函数,添加监听数据订阅者,数据变化通知更新视图。
订阅者: 1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
数据对象放到data中,然后observer用Object.keys(data)和forEach将data进行遍历
响应式 :把数据填充到页面叫响应式
为什么有key值?如果没有key值得话,diff算法会全部比较,会一项一项的比较,全部元素替换,没有做到最小更新。有了key值得话就更改一项。
如果删除前面的一项key值会发生变化,所以会被重新渲染,并且可能产生Bug
加索引行吗?不行,因为索引可能发生改变,加一个时间戳加key值。
通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信 桥梁,达到数据变化->视图更新在初始化vue实例时,遍历data这个对象,给每一个键值对利用Object.definedProperty对data的键值 对新增get和set方法,利用了事件监听DOM的机制,让视图去改变数据。
1、虚拟DOM本质就是用一个原生的JavaScript对象去描述一个DOM节点。是对真实DOM的一层抽象。 2、在浏览器中操作DOM是很昂贵的。频繁的操作DOM,会产生性能问题,所以需要这一层抽象,在patch过程中尽可能地一次性将差异更新到DOM中,这样保证了DOM不会出现性能很差的情况。 3、Vue2.x中的虚拟DOM主要是借鉴了snabbdom.js,Vue3中借鉴inferno.js算法进行优化。
JQuery是通过$直接操作DOM,vue不是直接更改DOM,vue通过虚拟DOM更改DOM,JQuery比vue更加轻量级更原生,
vue通过虚拟DOM更改数据大大减少性能损耗,为什么减少损耗?当视图发生改变时并不是直接更改DOM而是放在一个对象里,然后进行对比DOM改变,只有
children childNodes
入口 entery:“./pages”
出口 output: {
filename: 'index',
path: path.resolve(__dirname, '/dist')
}
loader:moludes: [
rules: [
{
test: /\.scss$/,
use: [
{
loader: "sass-loader" // 将 Sass 编译成 CSS
}
]
}
]
插件
浏览器将获取的HTML文档解析成DOM树。
处理CSS标记,构成层叠样式表模型CSSOM(CSS Object Model)。
将DOM和CSSOM合并为渲染树(rendering tree
),代表一系列将被渲染的对象。
渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout
。浏览器使用一种流式处理的方法,只需要一次绘制操作就可以布局所有的元素。
将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting
。当什么什么改变的时候才有重排和回流
HTML
html这标签语言真的基本没问啥
对语义化标签的理解 - 让人更可读,让机器更可读(利于SEO)
meta标签 - 问得多一点
CSS
flex布局,容器的属性,项目的属性 - 阮一峰老师的两篇文档,给我看!
实现两栏(左固定 右自适应)/三栏(左右固定 中间自适应)布局
常见伪元素、伪类,越多越好
垂直水平居中方法,越多越好
八种定位属性的具体含义 - static relative fixed absolute sticky inherit initial unset
选择器和优先级,权重- 这里继承优先级最低,那么哪些属性可继承?
回流重绘的概念、触发条件,避免回流重绘的操作,flush队列,display:none触发的是哪一个,visibility:hidden呢
两种盒子模型
BFC(块级格式上下文)的概念,触发条件,渲染规则,应用场景
移动端适配 - 这里涉及到的东西可就多了:媒体查询,viewport,em/rem,vw/vh
**
**
JavaScript
考察的更多的还是es6,什么let/const,数组对象的新方法,解构赋值,箭头函数,promise,async,generator,set,map之类的
数据类型,原始类型和引用类型,存储方式(堆、栈),判断数据类型的三种方法
知道哪些数组的方法 - 红宝书上超多个方法,过一遍
null和undefined的不同,为什么 == 结果是true,而 === 结果是false
闭包的概念,使用场景,缺点 - 我最认同《你不知道的JavaScript》里写的当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。是一种现象。
原型链
var let const 的不同 - 概念的不同,变量提升,暂时性死区,顶层对象
箭头函数和普通函数的区别,this的问题,为什么不能用作构造函数
promise - 为什么我就写了一个单词,因为我promise不太熟(。
this的问题 - 永远指向最后调用它的函数(es5),call/apply/bind改变指向(es6)
new 实例化一个对象 / new一个对象时发生了什么?四个步骤
防抖节流的使用,区别,手写
继承的方法
Eventloop,可以顺便说一下浏览器渲染进程,哪些是宏任务微任务 - 经典看代码写输出
事件流,事件代理解决的问题
深浅拷贝的区别,如何解决循环引用,手写实现 - https://segmentfault.com/a/1190000020255831经典好文!不得不看!
load 和 DOMContentLoaded(DOM Ready)的区别 - 看红宝书
引用script标签时涉及到的defer async
BOM:window location navigator - 解析url会用到location对象
JSBridge的原理及使用 - 因为我实习做的是APP内嵌页,涉及到与客户端通信
Vue
经常有面试官问我看过源码没,您好,没有(学习时间太短了,来不及嘛),所以面经笔记没有涉及到源码部分
顺便各位,读源码有什么建议吗,嗯看真是难下手
单页应用的优缺点
computed 和 watch 的区别
v-for 和 v-if 的区别
双向绑定的原理,哪些操作不会触发视图更新 - Vue3使用了proxy来代替传统的双向绑定
组件通信的方法,父子、隔代、兄弟,如果能再说说Vuex就更不错了
nextTick是干嘛的
vue-router,history hash 的区别,需不需要后端配置,守卫,路由钩子
写一个通用组件需要考虑哪些问题
**
**
计算机网络 浏览器
HTTP相关,请求头响应头构成,HTTP0.9/1.0/1.1/2 各版本的区别,默认端口80
HTTPS相关,与HTTP区别,默认端口443
状态码 1XX - 5XX,背就完事了
get / post 的区别
TCP / IP,OSI 七层模型 - 物理层 数据链路层 网络层 传输层 会话层 表示层 应用层
跨域相关,同源策略,跨域办法 - jsonp以及缺点 / webSocket协议 / CORS以及分类,预检请求
进程线程的概念
从输入URL到浏览器完成页面渲染发生了什么 - 被问烂了,越详细越好。这里又有web优化问题
强缓存,协商缓存 - 相关的头部、状态码
cookie、session、localStorage、sessionStorage
安全问题,XSS、CSRF概念,出现的场景,解决的办法 - XSS:CSP、HttpOnly Cookie;CSRF: samesite、Referer Check、Anti-CSRF-Token
**
**
手写
new 实例化一个对象 / new一个对象时发生了什么
继承
防抖节流
实现call、apply、bind
数组去重
深浅拷贝
事件代理
vue双向绑定
**
**
其他
下面这些是我了解过并写在简历上的,所以被问了
webpack,常用loader、plugin,如何配置
常用的 git 命令
linux基本指令
css预处理器相关,sass/less/postcss
使用 postman 来干啥
vim基本指令
git init 初始化
git status 查看文件目录与暂存区状态,看不到本地仓库
git add ‘文件名’ 添加到暂存区
git add . 添加所有到暂存区
git status 查看
git commit -m ‘文件名’ 已经到本地仓库里面了 (git commit -am ‘文件名’ add和commit的缩写 add 所有文件)
git commit -am ‘文件名’ 添加到暂存区并添加到本地仓库
git log 查看提交日志信息
git reflog 查看所有日志信息
–pretty=oneline 一行显示
git reset --hard + ‘唯一标识’
git checkout 回退
git rm ‘文件名’ 删除操作
git ls-files 查看git里面仓库目录
git config --global user.name ‘your_username’
git config --global user.email [email protected]
git config --list 查看所有配置 octotree
git push -u origin master 提交
git checkout ’分支名‘切换到指定分支
git branch 查看所有分支
git checkout -b ‘分支名’ 新建并切换到分支
切换到主干 合并分支 分支的作用是防止在线上出现bug
git branch -d '文件名
git push origin ‘分支名’ 添加到分支名
git reset --hard + ‘目标版本号’ git push -f 强推
git revert 反做
git revert -n 版本号”反做,并使用“git commit -m 版本名”提交
git pull和git fetch的区别?
git pull 等于先git fetch + git merge
git fetch 将远程仓库最新的数据拉取过来,
git merge 将拉取过来的数据和现在已经修改的分叉数据进行合并
git pull 可能会出现代码错误,最好是用git fetch 然后 git merge
git rebase 和 git merge的区别
一千万个数据如何找中位数
分治法
分治法的思想是把一个大的问题逐渐转换为规模较小的问题来求解。
对于这道题,顺序读取这 5 亿个数字,对于读取到的数字 num,如果它对应的二进制中最高位为 1,则把这个数字写到 f1 中,否则写入 f0 中。通过这一步,可以把这 5 亿个数划分为两部分,而且 f0 中的数都大于 f1 中的数(最高位是符号位)。
划分之后,可以非常容易地知道中位数是在 f0 还是 f1 中。假设 f1 中有 1 亿个数,那么中位数一定在 f0 中,且是在 f0 中,从小到大排列的第 1.5 亿个数与它后面的一个数的平均值。
提示,5 亿数的中位数是第 2.5 亿与右边相邻一个数求平均值。若 f1 有一亿个数,那么中位数就是 f0 中从第 1.5 亿个数开始的两个数求得的平均值。
对于 f0 可以用次高位的二进制继续将文件一分为二,如此划分下去,直到划分后的文件可以被加载到内存中,把数据加载到内存中以后直接排序,找出中位数。
注意,当数据总数为偶数,如果划分后两个文件中的数据有相同个数,那么中位数就是数据较小的文件中的最大值与数据较大的文件中的最小值的平均值。
查找英文文章里出现次数最多的字母和次数
模板字符串,es5实现
两个字符串找最大公共子串
生成一个数值区间在2-32的随机数组