浏览器机制
- 浏览器的线程、进程梳理
进程和线程进程相当于工厂,线程相当于工人。一个工厂内有一个或者多个工人。工厂拥有自己的资源(比如cpu,内存)浏览器有多个进程,基本上每个页面都是一个进程,其他的还有browser进程(负责页面交互显示,前进后退。管理各个tab页面进程的创建和销毁),网络资源进程(下载资源),第三方插件进程,gpu进程(至多一个),浏览器渲染进程
- 浏览器渲染进程包括:
- js引擎线程(所谓的js是单线程)
- gui用户界面渲染线程(跟js引擎互斥)
- 事件触发线程(setTimeout, 点击事件,ajax请求等)
- 定时器线程 (W3C的HTML标准中规定,setTimeout中低与4ms的时间间隔算为4ms)
- 异步请求线程
- webWorker
向浏览器申请子线程用于运算处理,跟主线程通信使用postMessage API(使用时要保证协议,端口,document.domain全部相同才可以通信)。可用于处理高耗时计算
- postMessage
使用 window.addEventListener(‘message’, handler, false) 进行监听数据传输,为了保证安全通过event的origin和source属性来判断 *** <font color=red>webWorker没有任何权限操作dom</font>
- shareWorker(兼容性极差)
实现多个tab页面之间的通信
- 浏览器的捕获和冒泡过程、如何禁止浏览器的默认行为、click事件、mouse事件、touch事件的执行顺序
- 概念:
“DOM2级事件”规定事件流包括三个阶段,事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的事件捕获,为截获事件提供了机会。然后是实际的目标接收了事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。(总结:从外到内再从内到外)
- 禁止冒泡:
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
- 取消默认事件:
w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
- event.stopImmediatePropagation
如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。如果其中某个监听函数执行了 event.stopImmediatePropagation() 方法,则当前元素剩下的监听函数将不会被执行。(译者注:注意区别 event.stopPropagation )
- 概念:
- 浏览器的数据存储(localStorage、sessionStorage、indexedDB、cookie)
- 浏览器的跨域问题以及解决方案
跨域:协议,端口,域名任意一个不同
-
async和defer脚本的区别
-
historyAPI
- 浏览器刷新率
60Hz,即1/60s刷新一次。
用setTimeout写动画会出现问题。应使用window.requestAnimationFrame(callback)的方式
监听动画执行使用动画监听事件
- DOM.addEventListener(‘animationstart’) // css动画开始后触发
- ‘animationiteration’ // css动画重复播放时触发
- ‘animationend’ // css动画结束时触
js、css
js:
- 基本类型与引用类型
- typeof
- instanceof
- Object.prototype.toString.call()
-
this指向问题(call, apply, bind)
- 面向对象问题
- es5、es6实现继承
- 原型链机制
- 闭包
- 垃圾回收机制
- 事件循环
- 概念: + js一直在执行一个类似于while true的循环。事件任务分为宏任务和微任务。执行顺序为宏任务 -> 微任务 -> 宏任务 -> 微任务 …
- 宏任务包括: + IO操作(ajax、点击事件等)、setTimeout、setInterval、setImmediate、requestAnimationFrame、 postMessage(在vue2.0的$nextTick中,就是优先使用微任务:promise再考虑宏任务setImmediate和MessageChannel的postMessage,最后才考虑使用setTimeout)
- 微任务包括: + process.nextTick、promise、MutationObserver(监听dom变化)、Object.observe
- 隐式类型转换
- [] == false 和 !![] == true
- a == 1 && a== 2 && a == 3
-
常用API(字符串,数组,对象,dom、 Date对象、 math对象)
-
正则表达式
- es6
- 箭头函数与普通函数对比
无法使用new this指向考点
- 手写promise
- map、set、proxy、迭代器函数、装饰器(使用es5实现?)
- 箭头函数与普通函数对比
- ajax
- 监听xml.onreadystatechange事件 + xml.readystate + 0 // 代理被创建,open方法未调用 + 1 // open()被调用 + 2 // send()方法被调用,响应头被接收 + 3 // 响应体正在被接收 + 4 // 数据传输完成
- xml.status 为响应中的状态码
- xml.upload 返回一个XMLHttpRequestUpload对象,监听该对象的‘progress’事件,可以获取上传进度
图片上传进度获取
- setTimeout和setInterval底层的区别
setInterval(() => {
fn() // 当这段代码执行的时间超过100ms时会怎么样
}, 100)
答:
假设在5ms时开始setInterval,设定105ms执行function block。但是由于205ms时,function block没有执行完毕,导致原本预定在205执行的function block被滞留,而js只允许一份function block被滞留,从而导致代码段执行丢失(应该是一个setInterval只允许一个,未试验)。 解决方案:使用setTimeout代替setInterval
css:
- 双飞翼布局实现方案
- 垂直水平居中
- 盒模型
- BFC
- 伪元素、伪类选择器
- css3 阴影
常用框架
-
vue的生命周期
-
vue的父子组件、兄弟组件传值
-
介绍vuex
-
vue中mixin和extend的区别
- vue-router部分
- 实现原理
- hash和history如何实现监听
- window.onhashchange和window.onpopstate (兼容性:当后端不做配置的时候,用history模式按f5刷新,会直接请求,有可能出现404。用hash模式就不存在这个问题)
- 导航钩子
- vue的双向绑定原理
- Object.defineProperty() 进行getter和setter的劫持 + 发布订阅模式
- vue2.0无法根据index修改数组的方案优化
- 利用proxy实现vue的框架优化
-
vue的diff算法
-
virtualDom
-
keepAlive
-
vue3.0新特性
- vue服务端渲染
网络
-
cookie和session
- 常见以及非常见http code
- 1xx(临时响应): 表示临时响应并需要请求者继续操作
- 100 (继续)
- 请求者应当继续请求,服务器返回此状态码表示已经接收到了第一部分,正在等待其余部分
- 101 (切换协议)
- 请求者已要求服务器切换协议,服务器已确认并准备切换
- 100 (继续)
- 2xx (成功)
- 200(成功)
- 服务器已经成功处理请求
- 201(已经创建)
- 请求成功并且服务器已经创建了新的资源
- 202(已接受)
- 服务器已经接受了请求,但是尚未处理
- 203(非授权信息)
- 服务器已经成功处理了请求,但返回的信息可能来自另一个来源
- 200(成功)
- 3xx(重定向)
- 301(永久移动)
- 302(临时移动)
- 303(查看其他位置): 请求者应当对不同位置使用单独的get请求来检索响应时,服务器返回此代码
- 304 (not modified): 被请求的网页未发生修改,可以直接读取浏览器缓存
- 305(使用代理): 请求者只能通过代理访问请求的网页(此时请求者应使用代理)
- 307 (临时重定向): 服务器目前从不同位置的网页响应请求,但请求者应继续使用原来位置来进行以后的请求
- 4xx(请求错误)
- 400(错误请求): 服务器不理解请求的语法
- 401(未授权): 请求要求身份验证
- 403(forbidden): 禁止访问~~~
- 405(方法不被允许): 禁用了请求中指定的方法
- 5xx(服务端错误)
- 500(服务器内部错误)
- 502(bad gate): 网关服务器从上游服务器接收到无效响应
- 503(不可用服务器)
- 504(网关超时)
- 505不支持所用的http协议版本
- 1xx(临时响应): 表示临时响应并需要请求者继续操作
- 缓存策略
- 强缓存(仅用于客户端)
- cache-control
- max-age public private no-cache no-store
- public表示可以被浏览器和代理服务器缓存,代理服务器一般可用nginx来做
- immutable 如果cahe-control:max-age=315360000,public再加个immutable的话,就算用户刷新页面,浏览器也不会发起请求去服务,浏览器会直接从本地磁盘或者内存中读取缓存并返回200状态,看上图的红色框(from memory cache)
- private 只让客户端可以缓存该资源;代理服务器不缓存
- no-cache
- 跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,只有在强缓存失效了才走协商缓存的,设置了no-cache就不会走强缓存了,每次请求都回询问服务端。
- no-store
- 不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了
- max-age public private no-cache no-store
- cache-control
- 协商缓存(用于服务器和客户端相互交互)
- etag:每个文件有一个,改动文件了就变了,就是个文件hash
-
last-modified:文件的修改时间,精确到秒(当文件被访问时,文件的last-modified会发生变化,所以并不准确)
- 每次请求返回来 response header 中的 etag和 last-modified,在下次请求时在 request header 就把这两个带上,服务端把你带过来的标识进行对比,然后判断资源是否更改了,如果更改就直接返回新的资源,和更新对应的response header的标识etag、last-modified。如果资源没有变,那就不变etag、last-modified,这时候对客户端来说,每次请求都是要进行协商缓存了
- 协商缓存流程:
-
服务器资源未过期:发请求–>看资源是否过期–>过期–>请求服务器–>服务器对比资源是否真的过期–>没过期–>返回304状态码–>客户端用缓存的老资源。
-
服务器资源过期:发请求–>看资源是否过期–>过期–>请求服务器–>服务器对比资源是否真的过期–>过期–>返回200状态码–>客户端如第一次接收该资源一样,记下它的cache-control中的max-age、etag、last-modified等。
-
- 强缓存(仅用于客户端)
- 从输入url到页面呈现发生了什么
- url -> ip DNS解析
- 发起http请求,涉及tcp/ip协议三次握手,假如是https还有一个证书交换,ssl
- 请求回来的字节流被浏览器进行词法解析,加载head中的链接
- html字符流被解析成dom树,进行渲染(domInteractive事件触发)
- css文件内容被解析成css树,对已有dom树进行渲染生成render树(domContentLoaded事件触发)
- 根据render树进行页面layout,确定dom元素的位置,大小
- 进行painting,绘制页面像素信息
- 浏览器根据上述信息发送给gpu,gpu进行信息合成,显示在屏幕上
- 下载对应图片完成(domComplete事件触发)
- 直到现在,js引擎执行load事件(执行加载完的js脚本(js线程和页面渲染线程互斥,所以script要贴着</body>尽量放下面))
页面性能
-
内存泄漏如何监听
-
页面启用硬件加速
- 页面的repaint和reflow
-
reflow(回流):当浏览器发现某个部分发生了点变化影响了布局,需要倒回去重新渲染,内行称这个回退的过程叫 reflow。reflow 会从 <html> 这个 root frame 开始递归往下,依次计算所有的结点几何尺寸和位置。reflow 几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显 示与隐藏)等,都将引起浏览器的reflow。鼠标滑过、点击……只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲 染。通常我们都无法预估浏览器到底会 reflow 哪一部分的代码,它们都彼此相互影响着。
-
repaint(重绘):改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸没有变。
注意:display:none的节点不会被加入Render Tree,而visibility: hidden则会,所以display:none会触发reflow,visibility: hidden会触发repaint。
-
- 页面性能优化点总结
- 性能测量
-
window.performance、 console.time() && console.timeEnd()
-
first paint性能优化
- 有缓存
- 优化点:页面缓存策略
- 无缓存
- url -> ip DNS解析
- 优化点:
- 使用dns预解析
- 由客户端控制主动通过请求dns获取域名ip,不走系统dns解析(具体方案待查)
- 优化点:
- http请求是基于tcp协议的,涉及tcp协议三次握手。假如使用https还包括ssl证书交换
- 优化点:
- 压缩文件大小
- 浏览器有并发最大连接数,合并js和css至html中
- 由服务端升级到http2(避免重复建立tcp连接)
- 使用gzip
- 使用cdn
- css spirite
- 图片懒加载,异步加载
- js根据路由异步加载(框架)使用defer和async(原生 两者的不同?)
- 小型图片使用data-uri
- 优化点:
- url -> ip DNS解析
- 有缓存
-
- 前端无限滚动下拉加载设计
- 问题分析:
- dom操作的性能消耗,而且dom操作是阻塞的。当一个dom操作在进行时,用户和页面交互无法进行(除了滚动页面)
- 视口外的图片的加载
- 滚动问题的节流和防抖
- 解决方案:
-
在viewport外的图片实现懒加载(需要一个阈值,在用户滚动到页面底部之前稍微提前一点进行加载)
-
滚动问题(截流和防抖)
-
Throttle允许我们限制激活响应的数量。我们可以限制每秒回调的数量。反过来,也就是说在激活下一个回调之前要等待多少时间;
- Debounce意味着当事件发生时,我们不会立即激活回调。相反,我们等待一定的时间并检查相同的事件是否再次触发。如果是,我们重置定时器,并再次等待。如果在等待期间没有发生相同的事件,我们就立即激活回调。
- 缓存ajax请求以及对应内容(by Asser)
- 将移除视口的dom进行remove (by Asser)
- 将每次拉取到的一批数据放在documentFragment里面再一起插入dom。减少页面回流 (by Asser)
-
-
- 问题分析:
- 性能测量
工程化
- webpack
-
主要分为入口,出口,loader、插件和模式五个部分。由于webpack只识别js,loader将入口文件直接引入和间接引入的非js文件,转化为webpack能处理的模块(module),
- loader有两个参数,test和use。test表示匹配到的文件,可以使用正则。use表示使用的loader。
- loader开发采用单一职责模式,基本loader都是通过链式调用的方式,只需要关心输入和输出。第一个loader处理原内容,之后每个loader处理的都是上一个loader的处理后的结果。(说白了就是一个函数来着)
- plugins需要通过require引用,然后放在plugins数组里,数组的每个item都是plugin文件的实例。在编译的过程中,webpack会在特定的时间点广播特定时间,然后plugin监听到对应时间就会执行对应逻辑。(plugin的目的主要是实现loader无法做到的其他事情)
class TestPlugin { constructor(successCb, failCb) { this.successCb = successCb this.failCb = failCb } apply(compiler) { compiler.plugin('done', state => { this.successCb(state) }) compiler.plugin('failed', state => { this.failCb(state) }) } }
-
-
在编写plugin时最常用的就是compiler和compilation。 + compiler可以理解为webpack实例,在webpack被启动的时候被实例化传给plugin。包含loaders, options,plugins这些信息。相当于webpack整个生命周期 + compilation表示当前的模块资源,变化的文件等。相当于一次新的编译
<font color=red>注意传给plugin的compiler和compilation都是同一个对象的引用!</font></br> - 常用webpack plugin loader
-
webpack uglifyjsPlugin, CommonChunkPlugin, extraTextplugin
-
webpack按需加载内部实现原理
-
安全
- xss原理以及防御
- csrf原理以及防御
计算机基础
- 内存存储(堆、栈)
- 排序算法
- 冒泡
function bubbleSort(arr) { for (let i = 0, len = arr.length; i < len - 1; i++) { for (let j = i + 1; j < len; j++) { if (arr[i] > arr[j]) { let temp = arr[i] arr[i] = arr[j] arr[j] = temp } } } }
- 冒泡
-
选择
-
插入
-
快速
-
合并
- Unicode和UTF-8字符串编码解码原理
- base64和二进制的不同
面试后补充:
+ dll-plugin
+ file-uploader 上传失败的监听
+ redux和vuex的对比
+ hash和history API的兼容性问题
+ jsonp的缺陷
+ vue3.0的新特性
+ node服务端渲染
+ node大文件上传
+ nextTick实现原理
参考链接:
被虐:
- vw,vh实现原理
- https 对称&非对称加密原理
- jsBridge实现方案(除开现有的注入)现有的注入底层的实现原理
- js浮点数bug内存存储原因
- 常见排序算法 时间空间复杂度如何计算
- Object._proto_
- typeOf Object
- 移动端适配方案(除开rem还有其他的吗)
- webpack打包之后的成果,pollyFill如何实现浏览器兼容
- leetCode编程练习
- 迭代器函数的使用(实现async await的封装)
- es6导入导出和amd导入导出的区别
可落地的发展方向:
- 一次编译多端实现(rn ,weex, flutter,小程序等)
- webpack自动化
- node后台方向
- polyFill手动实现
大厂要求
- 扎实的基础
- 对新技术时刻保持敏锐的嗅觉
- 对已经实现的技术底层原理保持好奇心
新技术 && 新特性
- webComponents
- GraphQL
- serverLess
- PWA桌面应用(electron)
- TypeScript
- WebAssembly
- Vue3.0
- Webpack5.0
- Http2.0
前端架构
- 框架技术选型
- 性能监控以及异常监控
- 前后端分离