参考八股文网上回答,有的自己加了一些描述和补充。
html
浏览器的渲染过程
1. 解析HTML,生成DOM树,解析CSS,生成CSSOM树
2. 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
3. Layout(回流 reflow):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
4. Painting(重绘 repaint): 根据渲染树以及回流得到的几何信息,得到节点的绝对像素
5. Display: 将像素发送给GPU,展示在页面上
重绘和回流
reflow: 得到节点位置信息和大小
repiant: 得到节点位置信息和rgba
回流常见情景
1. 添加、删除、替换dom 元素
2. 元素位置改变、大小改变(括外边距、内边框、边框大小、高度和宽度等)
3. 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
重绘常见情景
1. 元素背景、字体的 RGBA (某个div标签节点的背景颜色)
2. 回流一定引起重绘
script 的 async 跟 defer 的区别?
不配置这两个属性,碰到 script 会下载然后执行,阻塞 document 解析
async、defer 配置后都会异步下载,异步解析
async 下载完了就会解析(无序执行,哪个先下载好先执行)
defer 会等到 document 解析完,顺序执行脚本,然后触发 DOMContentLoaded 事件
js
事件循环
渲染引擎和 JS 引擎,js 和渲染是分开的。
JS 引擎处理 JavaScript 语言的一大特点就是单线程,同一个时间只能做一件事。为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程的阻塞,才有 Event Loop。
js:
1. 先会执行栈中的内容(先执行同步任务)
2. 栈中的内容执行后执行微任务
3. 微任务清空后再取出一个宏任务压入执行栈执行
4. 再去执行微任务 (回到 2)
5. 然后在取宏任务清微任务这样不停的循环。
宏任务:setTimeout setInterval setImmediate I/O postMessage
微任务:promise.then、resolve、mutationObersve、 async, await、process.nextTick :每个类型队列结束后执行
闭包
闭包:如果一个函数用到了外部的变量,那么该函数和这个变量就叫做闭包。
闭包隐藏细节就是,生成一个变量,一个函数去读这个变量。
好处:
1. 保存: 它不会被垃圾回收,因为那个变量没有离开执行环境
2. 保护: 可以理解成私有变量,只有用函数才能读取它,用户永远不能直接操作变量,必须通过函数来操作变量。
缺陷:内存泄漏
Promise 的理解
解决回调地狱,类似嵌套数组拍平
let、const、var 的区别;
es5: var, 没有块的概念, 作用在函数内,有变量提升,变量可以重复声明
es6: let、const 有块的概念,作用在 {} 内,变量不可以重复声明,会产生暂时性死区()
let const 区别,const 声明的变量,不能重新赋值(在栈中存储了指针不可变)
会产生暂时性死区:
暂时性死区是浏览器的bug:检测一个未被声明的变量类型时,不会报错,会返回undefined
如:console.log(typeof a) //undefined
而:console.log(typeof a)//未声明之前不能使用
let a
let /const/function会把当前所在的大括号(除函数之外)作为一个全新的块级上下文,应用这个机制,在开发项目的时候,遇到循环事件绑定等类似的需求,无需再自己构建闭包来存储,只要基于let的块作用特征即可解决
箭头函数和普通函数的区别
1. 箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this;
2. 没有this, 不能用 new,无法通过call bind apply 改变this
3. 没有arguments
4. 没有原型
5. 不能当作构造函数
this指向系列问题
全局变量:作为普通函数执行时,this指向window。
对象使用自身方法:当函数作为对象的方法被调用时,this就会指向该对象。
构造器调用,:this指向返回的这个对象。
箭头函数 :如果有嵌套的情况,则this绑定到最近的一层对象上。
基于Function.prototype上的 apply call 和 bind 调用模式:这三个方法都可以显示的指定调用函数的 this 指向。apply接收参数的是数组,call接受参数列表, bind方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。
原型和原型链, 继承(类、实例)
1. 所有对象都能共享原型上的方法,节省内存;
2. 通过找对原型链,方便地实现了继承。
function Person() {}
var person = new Person();
console.log(person.__proto__ === Person.prototype); //true
console.log(Person.prototype.constructor === Person); //true
person.name
先找实例属性或者方法 -> 找实例继承类的属性或者方法 -> 一直上层找 (找到最后 Object)
js有哪些数据类型?
基本类型(值类型):Number、String、Boolean、Symbol,null,undefined 栈内存储
引用类型(复杂数据类型):Object、Function、Array 堆内存储
const a = {}
a.key = '123'
// a = {b: 3}
原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,
将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。
当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
js有哪些判断类型的方法?
typeof
instanceof
Object.prototype.toString.call()
const type = (obj) => {
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}
typeof:不能正确判断null、Array,都会判断成object
原理:不同的对象在底层都表示为二进制,在Javascript中二进制低三位存储其类型信息。
000: 对象
001: 整数
010: 浮点数
100:字符串
110: 布尔
null和undefined的区别
undefined
1. 变量未定义
2. var 变量定义了,没有赋值
3. 函数没有return 返回值,默认返回陈结果为 undefined
4. 是一个表示“无”的原始值,转换为数字是NaN
null
1. 通常设置为对象变量的初始化
2. 是一个表示“无”的对象(空对象指针),转换为数字是0
会引起内存泄漏
全局变量
定时器
闭包
没有清理的dom元素,dom在作用域中使用了,dom删掉了,但还是在引用
垃圾回收 gc
GC:Garbage Collecation
垃圾收集器会定期(周期性)找出那些不再继续使用的变量,然后释放其内存。
标记清除:只要执行流进入相应的环境,就标记它,离开环境,就标记离开,
function test(){
var a = 10 ; // 被标记 ,进入环境
var b = 20 ; // 被标记 ,进入环境
}
test(); // 执行完毕 之后 a、b 又被标离开环境,被回收。
引用计数 当垃圾回收器下次再运行时,它就会释放那些引用次数为 0 的值所占用的内存。
function test() {
var a = {}; // a 指向对象的引用次数为 1
var b = a; // a 指向对象的引用次数加 1,为 2
var c = a; // a 指向对象的引用次数再加 1,为 3
var b = {}; // a 指向对象的引用次数减 1,为 2
}
循环引用。如果 fn 函数被大量调用,就会造成内存泄露。 在 IE7 与 IE8 上,内存直线上升。
function fn() {
var a = {};
var b = {};
a.pro = b;
b.pro = a;
}
fn();
sessionStorage、localStorage、cookie
(1)存储大小:cookie一般不超过4k,sessionStorage、localStorage 5M或者更大
(2)数据有效期:cookie若没设置时间关闭浏览器cookie失效,若设置了时间cookie就会存放在硬盘里过期才失效,
sessionStorage 关闭页面或者浏览器就被清除,localStorage 永久有效除非手动清除;
(3)作用域:cookie、localStorage 同源窗口共享 ,sessionStorage在同一浏览器窗口是共享的;
(4)通信:cookie传递于服务器和浏览器之间,localStorage、sessionStorage仅在浏览器保存
浏览器读取缓存的顺序:memory –> disk。
from memory cache 内存缓存
特点:将编译解析后的文件(如:js 和图片)存在该进程的内存中,方便下次快速读取,进程关闭,内存清空。
from disk cache 硬盘缓存
特点:需要对该缓存存放的硬盘文件进行 I/O 操作,然后重新解析内容(如:css),读取复杂,速度比内存缓存慢。
sessionStorage: memory cache
localStorage: disk cache
防抖节流
防抖
生活例子: 坐电梯,按电梯键
- 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
- 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多
- 文本编辑器实时保存,当无任何更改操作一秒后进行保存
- DOM 元素的拖拽功能实现
- 计算鼠标移动的距离
- Canvas 模拟画板功能
- 搜索联想
节流:
生活例子: 玩游戏,技能到了100%,才能发技能
- scroll 事件,每隔一秒计算一次位置信息等
- 浏览器播放事件,每个一秒计算一次进度信息等
- input框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求
- 滚动加载;mousemove ,mousehover
e6有哪些东西
变量声明:let const
函数:箭头函数
模板字符串
新的数据类型:
bigInt:用于解决大于16 位的整数以及大于或等于2的1024次方的数值无法精确表示的问题
Symbol:表示独一无二的值。可用于对象的属性,防止属性名的冲突
解构赋值
rest运算符
循环:for in/of
数组新增方法:find/findIndex includes
对象新增方法:
1. Object.assign()
2. Object.is()
3. Object.setPrototypeOf(object, prototype)和Object.getPrototypeOf() 读取或设置当前对象的原型对象
数据结构:
Set 类似于数组,但是成员的值都是唯一的,没有重复的值。
Map 它类似于对象,也是键值对的集合
promise: Promise 是异步编程的一种解决方案。回调地狱:异步请求套异步请求
async和await
css
水平垂直居中
.parent {
position: relative;
}
.child {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
.parent {
display: flex;
/* 定义项目在主轴上如何对齐 */
justify-content: center;
/* 定义项目在交叉轴上如何对齐 */
align-items: center;
}
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
bfc
独立元素不会被外界元素污染
body 根元素
浮动元素:float 除 none 以外的值
绝对定位元素:position (absolute、fixed)
display 为 inline-block、table-cells、flex
overflow 除了 visible 以外的值 (hidden、auto、scroll)
盒子模型
css盒模型是由content、padding、border、margin组成
box-sizing
content-box:使元素的宽高即为内容区域的宽高(默认)
box-sizing:content+padding+border=本身元素大小,即会缩小content的大小
inherit:指定box-sizing属性的值,应该从父元素继承
响应式设计的基本原理是什么
响应式设计是一个网站能够兼容多个终端,而不是为每一个终端做一个特定的版本。
基本原理是通过媒体查询检测不同的设备屏幕尺寸做处理
http
强缓存和协商缓存
强缓存
Response Header 设置 Cache-Control 和 Expires
优先级:Cache-Control > Expires
Expires(HTTP1.0):缓存的到期时间,若请求时间小于该时间,则直接使用缓存结果。 使用时间比较缺点:若客户端与服务端的时间由于时区或其他原因不一致,则缓存直接失效
Cache-Control(HTTP1.1,目前浏览器):
public:所有内容都将被缓存(客户端和代理服务器都可缓存)
private(默认):所有内容仅客户端可缓存
no-cache:客户端缓存内容,通过协商缓存来决定是否缓存
no-store:不强制缓存,也不协商缓存
max-age=xxx :缓存内容将在本次请求 xxx 秒后失效,xxx 秒内强制缓存生效
解决兼容问题:
1. 缓存问题
协商缓存
当强缓存失效或未设置时,进入协商缓存流程
协商缓存生效,返回 304
协商缓存失效,返回 200 和请求结果
优先级:Etag / If-None-Match > Last-Modified / If-Modified-Since
Last-Modified: response header 设置,表示:该资源文件在服务器最后被修改的时间。
If-Modified-Since:request header 设置,上次请求响应中的 Last-Modified
服务端将两者对比后决定返回 200 或 304
Etag:资源文件的一个唯一标识(由服务器生成) response header 设置
If-None-Match:上一个 Etag,request header 设置
服务端将两者对比,一致则返回 304,不一致则返回 200,和新的资源
为什么要有 etag
某些服务器不能精确的得到文件的最后修改时间。
某些文件修改非常频繁
浏览器输入 url 到页面展现的过程
- 查找缓存
- DNS解析 域名 -> IP地址
- 建立TCP连接,三次握手
- HTTP请求
- 服务器响应请求并返回结果
- 关闭TCP连接,四次挥手
- 浏览器渲染
- 构建DOM树
- 构建CSS规则树
- 合并生成render树
- 遇到<script>则暂停渲染,优先加载并执行JS代码,完成再继续
- 布局-绘制
上面是 html 当解析字符串 script 字段的时候,同理请求,后端响应数据
当然响应的肯定是js 文件,那么要做 tokenier 操作,将code 字符串转 ast, 然后做parse 操作 将 ast 转响应函数变量等,并执行
强制缓存失效->携带缓存标识向服务器发起请求->返回304,协商缓存生效-协商缓存失效,返回200和请求结果
跨域
游览器安全性考虑,同源问题,
跨域是由浏览器的同源策略造成,(协议、域名、端口)有一个不同就视为跨域
jsonp 需要前后端定好规则
去创建一个script标签
script的src属性设置接口地址
接口参数,必须要带一个自定义函数名,要不然后台无法返回数据
通过定义函数名去接受返回的数据
缺陷: JSON 只支持 get,因为 script 标签只能使用 get 请求; JSONP 需要后端配合返回指定格式的数据
CORS(Cross-origin resource sharing)跨域资源共享 服务器设置对CORS的支持原理:服务器设置Access-Control-Allow-Origin HTTP响应头之后,浏览器将会允许跨域请求
proxy 代理 目前常用方式,通过服务器设置代理
ng
webpack
TCP 和 UDP 的区别
TCP是面向链接的,而UDP是面向无连接的。
TCP仅支持单播传输,UDP 提供了单播,多播,广播的功能。
TCP的三次握手保证了连接的可靠性; UDP是无连接的、不可靠的一种数据传输协议,
UDP的头部开销比TCP的更小,数据传输速率更高,实时性更好。
HTTP 和 HTTPS 的区别
HTTPS 中,使用传输层安全性(TLS)或安全套接字层(SSL)对通信协议进行加密。
HTTPS结合了对称加密和非对称加密的特点,在客户端和服务端第一次通信时候,采用非对称加密的形式完成随机密钥的传输,在后面的请求中,采用首次生成的密钥进行对称加密
https://github.com/liuyib/note/blob/master/%E7%BD%91%E7%BB%9C%E7%9B%B8%E5%85%B3/HTTP(S)%20%E7%9B%B8%E5%85%B3/%E6%B7%B1%E5%85%A5%E6%8E%A2%E7%A9%B6%20TLS%20%E5%8D%8F%E8%AE%AE.md
端口:http 80, https 443
安全:http 明文传输, https 加密传输,使用传输层安全性(TLS)或安全套接字层(SSL)对通信协议进行加密。
大小:http 肯定小一点啊,https 都多了一层ssl 或者 tsl
费用:http 肯定小一点啊,https 都要提供一个 网站证书
https 协议的工作原理
客户端使用 https url 访问服务器,则要求 web 服务器建立 ssl 链接。
客户端浏览器通过双方协商一致的安全等级,建立会话密钥,然后通过网站的公钥来加密会话密钥,并传送给服务器。
web 服务器通过自己的私钥解密出会话密钥
web 服务器通过会话密钥加密与客户端之间的通信
http常见状态码有哪些?
消息 100~199
成功 200-299
重定向 300 -399
客户端出错 400-499
服务器出错 500-599
2xx
200 成功
204 成功,但没有数据返回
206 成功,但返回部分数据(数据范围由 Content-Range 指定)
3xx
301 永久重定向(以 GET 方法重新请求)
302, 303 临时重定向(以 GET 方法重新请求)
307 临时重定向(以 POST 方法重新请求)
304 资源未更新,应使用缓存
4xx
400 请求报文中存在语法错误
401 没有认证(身份)或认证失败
403 没有权限
404 未找到资源
5xx
500 服务器处理请求时报错
503 服务器无法处理请求(超负载或停机维护)
http各版本的改进都是什么
http1.0
主要使用 header 里的 If-Modified-Since(协商缓存请求标识), Expires(强缓存) 来做为缓存判断的标准
支持get post 方法
http1.1
cache Control、 Etag、If-None-Match 等更多可供选择的缓存头来控制缓存策略
支持长链接Connection: keep-alive, 一次TCP链接多次请求
断点续传,传完之后返回状态码206
支持新的方法 PUT、HEAD、OPTIONS 等,可用于restful api
http2.0
可以压缩header减少体积
新的二进制格式(Binary Format):2.0 基于二进制解析,1.x 基于文本解析
多路复用: 一个request对应一个id,这样一个连接上可以有多个request(类似CPU时间片)一次tcp连接中可以多个http并行请求
降低了慢启动的影响(只有一个链接,越来越快)
服务端推送
需要主动配置(nginx: http2_push /style.css;
其他介绍: http2 中多路复用
在 HTTP/1 中,每次请求都会建立一次 HTTP 连接,也就是我们常说的 3 次握手和 4 次挥手,这个过程在一次请求过程中占用了相当长的时间,即使开启了 Keep-Alive,解决了多次连接的问题,但是依然有两个效率上的问题,一是串行的文件传输,二是连接数过多导致的性能问题。
HTTP/2 的多路复用就是为了解决上述的两个性能问题。
在 HTTP/2 中,有两个非常重要的概念,分别是帧(frame)和流(stream)。帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。
多路复用,就是在一个 TCP 连接中可以存在多条流。换句话说,也就是可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。
Post 和 Get 的区别
定义上:
GET 不会修改服务器状态,是幂等请求
POST 会修改服务器状态,非幂等(对应的是 PUT,修改但幂等)
幂等: 大量请求,响应结果一样
幂等: get 幂等,post 幂等
位置:get 将数据放在 url 中 (请求的路径中),post 放在 请求body 里面
缓存:由于get 将数据放在 url 中,会有历史记录,而post 没有
编码:get 只能进行 URL 编码,只能是 ASCII 字符,POST 没有限制
大小:URL 最大长度会有限制,所以get
安全:由于get 将数据放在 url 中,post 放在 请求body 里面,所有 get 更安全
没办法,八股文就是八股文,你不用上面的说法说,面试官不买账
严格来说,GET 和 POST 都是不安全的,它们都是明文传输。要想安全,使用 HTTPS。
dns
用户访问 LDNS(本地DNS服务)
不成功请求远程 DNS 服务,返回一条 CNAME 记录,指向 CDN 服务商的 DNS 调度服务对应的 ip
访问 DNS 调度服务,获取合适的边缘节点 ip 地址
访问边缘节点,获取资源,没有则进行回源,获取源站的资源
DNS查询
- 首先查询浏览器缓存,是否存在domain对应的服务器IP
- 没命中,则查找操作系统缓存,是否存在
- 没命中,操作系统将域名转发给本地域名服务器,本地域名服务器自身进行递归查询
- 没命中,本地域名服务器向上级域名服务器进行迭代查询,过程如下
- 本地域名服务器向根域名服务器发请求,根域名服务器返回顶级域名服务器的地址
- 本地域名服务器向顶级域名发请求,得到权限域名服务器地址
- 本地向权限域名服务器发请求,得到域名对应的服务器的IP
- 本地得到IP后,缓存一份并下发给操作系统缓存,再下发到浏览器缓存
vue
mvvm、双向绑定的原理
MVVM 在使用当中,利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,
而 ViewModel 变化时,View 也会自动变化。
vue2:
Vue 使用 Object.defineProperty 遍历和递归对data中的所有属性做数据劫持,把这些 属性 全部转为 getter/setter,当获取的时候会触发getter,设置的时候会触发setter
其他:
compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,
添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,
最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效
data 为什么返回一个函数
$nextTick 的实现原理
在下次 DOM 更新循环结束之后执行延迟回调。
因为Vue 在更新 DOM 时是异步执行的,当我们设置一个属性变更时,组件不会立即渲染,所以我们在同步代码中获取不到变更后的渲染结果。
而nextTick函数内部是在用微任务去执行所有的更新函数,而我们的Vue.nextTick传入的回掉函数也会被加入到以下的callbacks数组中,也就变成了异步执行函数
注意⚠️:Vue内部也做了兼容降级处理,依次是
Promise - MutationObserver - setImmediate - setTimeout0秒
v-for 中 key
1. 提供的key属性,它可以让你自己决定是否要复用元素,key的值必须是唯一的
2. 新旧vdom直接比较,如果有key, 相应的内容相当于 map 进行比较,如果没有那么遍历了
3. 如果不设置key,会最大程度重用之前的dom,但是有时会有问题(举例:选中第一个checkbox,此时再往前添加一个checkbox,会导致新增的box选中,原因是重用了之前的dom,只更新了文本内容 )
4. 作用: 减少渲染次数,提升渲染性能
https://juejin.cn/post/6962699982293958692
组件通信的方式
1、props
2、$on、$emit
3、$parent、$children、$refs
4、Event Bus ==> Vue.prototype.$bus = new Vue()
5、$attrs、$listeners
6、Provide、inject
7、vuex
computed 和 watch 的区别
watch主要监听属性变化,多个数据影响一个数据,不读区缓存,而computed主要是一个值收到多个值的影响,默认读取缓存
为什么需要虚拟 DOM
hash 和 history 分别是基于什么实现的
前端路由实现的本质是监听 url 变化,实现方式有两种:Hash 模式和 History 模式,无需刷新页面就能重新加载相应的页面。
Hash url 的格式为www.a.com/#/,当#后的哈希值发生变化时,通过 hashchange 事件监听,然后页面跳转。
History url 通过history.pushState和history.replaceState改变 url。 两种模式的区别:
hash 只能改变#后的值,而 history 模式可以随意设置同源 url;
hash 只能添加字符串类的数据,而 history 可以通过 API 添加多种类型的数据;
hash 的历史记录只显示之前的www.a.com而不会显示 hash 值,而 history 的每条记录都会进入到历史记录;
hash 无需后端配置且兼容性好,而 history 需要配置index.html用于匹配不到资源的情况。
说说Vue的keep-alive使用及原理。
组件保留在内存中,防止重复渲染dom,减少损耗,提高体验。原理就是将需要缓存的vnode节点保存在this。cache中,生成虚拟dom的时候如果符合就直接使用
vuex
action处理异步,mutations修改值,state储存数据,getters处理格式
State(render) => vue component(dispatch) => actions (commit) => mutation (mutate) => state => ....
核心概念:
1,state, 初始化一个Vuex.Store保存所有的组建的公共数据。
2,getters, 所有组建的computed属性。也就是store的计算属性。getters 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
3,Mutations, store中的methods,有两个参数 第一个参数是state, 第二参数是payload自定义参数
4,Actions, 类似于mutations,可以异步操作,mutations不可以
5,Modules Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
diff算法
diff算法是平级比较,不考虑跨级比较的情况。内部采用深度递归的方式
react
redux
fiber
一种可中断的更新机制,也可以说是jsx被处理成虚拟dom之后的新的数据结构。每个元素都会被处理成一个fiber结构的数据,用链表形式连接,整个dom结构就形成了fiber树
根本原因,是大量的同步计算任务阻塞了浏览器的 UI 渲染。默认情况下,JS 运算、页面布局和页面绘制都是运行在浏览器的主线程当中,他们之间是互斥的关系。如果 JS 运算持续占用主线程,页面就没法得到及时的更新。当我们调用setState更新页面的时候,React 会遍历应用的所有节点,计算出差异,然后再更新 UI。如果页面元素很多,整个过程占用的时机就可能超过 16 毫秒,就容易出现掉帧的现象。
Fiber树:React 在 render 第一次渲染时,会通过 React.createElement 创建一颗 Element 树,可以称之为 Virtual DOM Tree,由于要记录上下文信息,加入了 Fiber,每一个 Element 会对应一个 Fiber Node,将 Fiber Node 链接起来的结构成为 Fiber Tree。Fiber Tree 一个重要的特点是链表结构,将递归遍历编程循环遍历,然后配合 requestIdleCallback API, 实现任务拆分、中断与恢复。
https://juejin.cn/post/7016593221815910408?share_token=422b150a-7afe-4349-8356-f51dd75e77d4#heading-85
webpack
做过哪些化方案
1. 多线程/多实例构建
2. 缩小打包作用域:resolve.modules 指明第三方模块的绝对路径
3. 充分利用缓存提升二次构建速度
4. 使用 cache-loader 或者 hard-source-webpack-plugin
注意:thread-loader 和 cache-loader 兩個要一起使用的話,請先放 cache-loader 接著是 thread-loader 最後才是 heavy-loader
webpack的loader和plugin区别是什么?常用的plugin和loader有哪些?
webpack自身只支持js和json这两种格式的文件,对于其他文件需要通过loader将其转换。
loader,它是一个转换器,将A文件进行编译成B文件,单纯的文件转换
file-loader:把⽂件输出到⼀个⽂件夹中
url-loader:和 file-loader 类似,base64 的⽅式
source-map-loader:加载额外的 Source Map ⽂件,以⽅便断点调试
babel-loader:把 ES6 转换成 ES5
css-loader:加载 CSS,⽀持模块化、压缩、⽂件导⼊等特性
style-loader:把 CSS 代码注⼊到 JavaScript 中,通过 DOM 操作去加载 CSS。
eslint-loader:通过 ESLint 检查 JavaScript 代码
thread-loader(多进程loader)
plugin是工具不操作文件,在webpack打包过程中,执行一些任务(打包优化、文件管理、环境注入)
ProvidePlugin:自动加载模块,代替require和import
html-webpack-plugin 可以根据模板自动生成html代码,并自动引用css和js文件
clean-wenpack-plugin 清理每次打包下没有使用的文件
HotModuleReplacementPlugin 热更新
optimize-css-assets-webpack-plugin 不同组件中重复的css可以快速去重
compression-webpack-plugin 生产环境可采用gzip压缩JS和CSS
loader:模块转换器。
将非 js 模块转化为 webpack 能识别的 js 模块,将 A.less 转换为 A.css。
webpack 自身只支持 js 和 json 这两种格式的文件。
loader 的执行顺序和配置中的顺序是相反的,即最后一个 loader 最先执行,第一个 loader 最后执行。第一个执行的 loader 接收源文件内容作为参数,其它 loader 接收前一个执行的 loader 的返回值作为参数,最后执行的 loader 会返回此模块的 JavaScript 源码。
本质:将所有类型的文件转换为应用程序的依赖图可直接引用的模块。
plugin: 拓展
针对 loader 结束后,基于事件机制工作,会监听 webpack 打包过程中的某些节点。 在 webpack 运行的生命周期中会广播出许多事件,plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 API 改变输出结果。
compliation:包含当前模块资源,编译生成资源,webpack 开发模式下运行时,当检测到任意一个文件变化,都会创建一次新的 compliation
webpack构建流程是什么?
1、初始化参数,合并传入的和webpack.config.js文件中的配置参数
2、注册/监听 插件,监听构建的生命周期
3、拿到配置从入口处开始构建这个AST语法树,它会根据依赖一直递归下去
4、webpack默认只能编译js和json,遇到其他文件类型会根据配置的loader进行转换
5、得到文件的结果和依赖关系后生成代码块chunk
6、输出到dist目录
初始化参数:shell weback.config.js
开始编译:(启动 webpack):初始化一个 compiler 对象,加载配置,开始执行编译
compiler 对象:全局唯一,是 webpack 的实例,包含了 webpack 环境的所有配置信息
确定入口:根据 entry 中的配置,找出所有入口文件
编译模块:从入口文件开始,调用所有 loader,再递归找依赖
完成模块编译:得到每个模块被翻译后的最终内容以及他们之间的关系(依赖图)
输出资源:根据依赖关系组装成一个个包含多个 module 的 chunk
输出完成:根据配置,确定输出的文件名以及路径
treeShaking机制的原理
利用es6模块的规范
ES6 Module引入进行静态分析,编译的时候判断到底加载了那些模块
判断那些模块和变量未被使用或者引用,然后删除对应代码
Tree Shaking
用来移除 JavaScript 上下文中的未引用代码(dead-code),是 DCE 的一种新实现,所有导入文件都会受到 tree shaking 的影响。
其消除原理依赖于 ES6 的模块特性,ES6 模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这是 tree-shaking 的基础。
DCE
Dead Code Elimination——消除死代码
传统编译型的语言中,都是由编译器将 Dead Code 从 AST(抽象语法树)中删除,js 需要压缩工具 uglify 做处理(webpack 自身集成 uglify)。
Dead Code 一般具有以下几个特征:
• 代码不会被执行 • 代码执行的结果不会被用到
• 代码只会影响死变量(只写不读)
// 全部导入 (不支持 tree-shaking)
import _ from "lodash";
// 具名导入(支持 tree-shaking)
import { debounce } from "lodash";
// 直接导入具体的模块 (支持 tree-shaking)
import debounce from "lodash/lib/debounce";
webpack 热跟新
1、首先启动dev-server,webpack开始构建时会向 入口 文件注入热更新代码
2、浏览器打开的时候,浏览器与本地服务会基于Socket建立通讯
3、本地服务会监听文件的改动,然后会再次编译
4、编译完成后通过socket 发送消息告诉浏览器,(通过维护hash值和state状态);
5、浏览器再去请求新的模块替换掉之前旧的模块
webpack 的hash
1、hash: 每次webpack编译中生成的hash值
2、chunkhash: chunkhash基于入口文件及其关联的chunk形成,文件的改动只会影响与它有关联的chunk的
hash值,不会影响其他文件
3、contenthash: contenthash根据文件内容创建。当文件内容发生变化时,contenthash发生变化
性能和优化
1. 减少请求:多张图片合成一张,base64
2. 缓存:对常用不变的资源进行缓存
3. 延迟加载的方式,节流防抖,
4. 对某些资源使用预加载的方式
5. 减少 重排重绘操作
6. 合并代码, ssr服务端渲染
7. http开启gzip压缩
8. CDN可以根据当前用户的距离和网络,把请求导向到最近的服务节点上
9. vue:
vue-router使用懒加载
合理使用watch和computed
v-for必须添加key
销毁定时器
keep-alive
10. webpack
Tree Shaking
提取公共第三⽅库: SplitChunksPlugin插件来进⾏公共模块抽取,可以⻓期缓存这些⽆需频繁变动的公共代码
安全
xss 跨站请求攻击(Cross Site Scripting 跨站脚本攻击
是一种跨站执行的脚本,也就是javascript脚本,指的是在网站上注入我们的javascript脚本,执行非法操作
在线网址: http://www.jsfuck.com/
将重要的cookie 设置成http only 这样就不能通过js获取到该cookie了
转义<script>标签、对用户输入的script进行移除
XSRF:跨站请求伪造 (Cross Site request forgery 跨站请求伪造)
是一种跨站的伪造的请求,指的是跨站伪造用户的请求,模拟用户的操作
1、使用token
SQL注入
SQL注入是一种常见的Web安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击。
所有的查询语句建议使用数据库提供的参数化查询接口,不要直接拼接 SQL 语句,
对进入数据库的特殊字符(',",,<,>,&,\*,; 等)进行转义处理,或编码转换,比如 lodash 的 lodash._escapehtmlchar 库
给此用户提供仅仅能够满足其工作的最低权限,从而最大限度的减少注入攻击对数据库的危害
node
- AMD(Modules/Asynchronous-Definition)、CMD(Common Module Definition)
Asynchronous Module Definition,异步模块定义,所有的模块将被异步加载,模块加载不影响后面语句运行。所有依赖某些模块的语句均放置在回调函数中。
区别:
1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
2. CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:
// CMD sea.js
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
// 此处略去 100 行
var b = require('./b') // 依赖可以就近书写
b.doSomething()
// ...
})
// AMD 默认推荐 require.js
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething()
// 此处略去 100 行
b.doSomething()
// ...
})
es moudle
其他
介绍一个项目
- 讲编译原理知识吧
- 讲自己造的轮子(各种框架系列)
项目中遇到的问题/困难(必问) 大多数困难问题解决方法
- 复现
- 定位,搜索网上出现相应问题的解决方法
- 修改代码
- 查看是否解决
- 解决之后是否可以整理出方案
平时的学习方式;最近学到的东西
- 看技术博客
- 造轮子
你的职业规划是什么
- 两到三年内,成为一名高级程序员
- 之后有时间,学习后端技术,这样方便与后端同事更好的对接问题