HTTP缓存:
强缓存
- Expires:[<http-date>(绝对时间)]
- Cache-Control(通用消息头):[max-age=<seconds>(相对时间),no-cache(协商缓存验证),no-store(不使用任何缓存)]
//重新加载: 请求通过 `If-None-Match` 和 `If-Modified-Since` 进行验证,状态码304或200。
fetch("https://developer.mozilla.org/static/js/ga.js", {
headers:{
'Cache-Control':'max-age=0'
}
});
fetch("https://developer.mozilla.org/static/js/ga.js", { cache: "no-cache" });
//强制重新加: 服务器不进行新鲜度验证,状态码200。
fetch("https://developer.mozilla.org/static/js/ga.js", {
headers:{
'Cache-Control':'no-cache'
}
});
fetch("/", { cache: "reload" });
协商缓存
- Last-Modified:资源最近修改时间,由服务器告诉浏览器。
- If-Modified-Since:资源最近修改时间,由浏览器告诉服务器。
- Etag:资源标识,由服务器告诉浏览器。
- If-None-Match:缓存资源标识,由浏览器告诉服务器。
- Vary:让代理服务器的缓存命中更多的决定因子,而不仅仅是依据请求 URL 和请求方法来决定是否命中。
- Age:对象在缓存代理中存贮的时长,以秒为单位。
一般情况下html文件不进行缓存(Cache-Control:no-cache),js.css等静态文件采用缓存。
cookie
同站(same-site)/第一方(first-party):有效顶级域名和二级域名相同(www.a.taobao.com,www.b.taobao.com)
跨站(cross-site)/第三方(third-party):有效顶级域名或二级域名不相同(www.taobao.com,www.baidu.com,b.github.io,a.github.io)
Set-Cookie:[<cookie-name>=<cookie-value>; Domain=<domain-value>; Max-Age=;Expires=; Secure; HttpOnly]
- Domain:指定cookie所属域名,默认是当前域名。
- Path:指定cookie在哪个路径下生效,默认是 '/' 。
- MaxAge:cookie失效的时间,单位秒。(相对时间)
- Expires:cookie过期时间。(绝对时间)
- Secure:该cookie仅被使用安全协议传输(https,ssl)。
- HttpOnly:仅用于网路传输,禁止JS脚本读取到该cookie的信息。
- SameSite:可以让Cookie在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
- Strict:禁用第三方cookie
- Lax: 特定条件下允许第三方cookie(外部站点导航到源站)
- None: 完全允许第三方cookie
设置规则:在当前域名下,只能设置当前域以及父域的cookie,不能设置子域下的cookie。
访问规则:cookie挂载在某个域下,只有在此域名下或者此域名的子域下才能获取cookie。
事件循环(Event Loop)
宏任务
- script
- setTimeout/setInterval
- setImmediate
- I/O
- UI事件
- postMessage
微任务
- Promise
- async/await
- process.nextTick(NodeJs)
- Object.observe(已废弃)
- MutaionObserver
async function async1() {
console.log('1');
await new Promise((resolve) => {
console.log('2');
resolve();
}).then(() => {
console.log('3');
})
console.log('4');
}
async1();
//执行顺序: 1 2 3 4
setTimeout(function () {
console.log('6');
}, 0)
console.log('1');
async function async1() {
console.log('2');
await async2();
console.log('5');
}
async function async2() {
console.log('3');
}
async1();
console.log('4');
//执行顺序: 1 2 3 4 5 6
console.log('1');
async function async1() {
console.log('2');
await 'await的结果';
console.log('5');
}
async1();
console.log('3');
new Promise(function (resolve) {
console.log('4');
resolve();
}).then(function () {
console.log('6');
})
//执行顺序 1 2 3 4 5 6
setTimeout(function () {
console.log('9');
}, 0)
console.log('1');
async function async1() {
console.log('2');
await async2();
console.log('8');
}
async function async2() {
return new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('6');
})
}
async1();
new Promise(function (resolve) {
console.log('4');
resolve();
}).then(function () {
console.log('7');
})
console.log('5');
//执行顺序 1 2 3 4 5 6 7 8 9
new Promise(function (resolve,reject) {
console.log(1);
new Promise(function (resolve,reject) {
console.log(2);
setTimeout(function(){
resolve(3);
console.log(4);
})
}).then(function(data){
setTimeout(function(){
console.log(5);
})
console.log(data);
})
setTimeout(function(){
resolve(6);
console.log(7);
})
}).then(function(data){
console.log(data);
setTimeout(function(){
console.log(8);
})
console.log(9);
})
//执行顺序 1 2 4 3 7 6 9 5 8
渲染原理
- GUI渲染线程: GUI 渲染线程负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等。当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。
- JavaScript引擎线程: JavaScript 引擎线程主要负责解析 JavaScript 脚本并运行相关代码。
- 事件触发线程:当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。
- 定时器触发器: 我们日常使用的
setInterval
和setTimeout
就在该线程中。 - Http请求线程: 在
XMLHttpRequest
在连接后是通过浏览器新开一个Http请求线程请求。
- 构建DOM树:构建DOM过程中,如果遇到
<script>
标签,渲染引擎会停止对HTML的解析,而去加载执行JS代码,原因在于JS代码可能会改变DOM的结构。 - 样式计算:根据CSS代码确定每个DOM节点的计算样式(computed style)。
- 生成布局树:主线程会遍历DOM及相关元素的计算样式,构建出包含每个元素的页面坐标信息及盒子模型大小的布局树(跳过隐藏的元素
display: none
)。 - 生成图层树:浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面。
- 显示合成:
- HTML根元素
position
不为static
并且设置了z-index
属性opacity
|transform
|filter
|isolation
will-change
- 需要剪裁的地方
- 隐式合成:当出现一个合成层后,层级顺序高于它的堆叠元素就会发生隐式合成。
- 显示合成:
回流(重排)
对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生回流过程。
- 一个 DOM 元素的几何属性变化(
width
,height
,padding
,margin
,left
,top
,border
等) - 使 DOM 节点发生
增减
或者移动
- 读写
offset
族、scroll
族和client
族属性的时候 - 调用
window.getComputedStyle
方法
重绘
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color
、background-color
、visibility
等)。
重绘不一定导致回流,但回流一定发生了重绘。
Web Worker
- Worker:一个专用 worker 仅仅能被首次生成它的脚本使用。
//main.js
//将函数转成URL对象
function fn2workerURL(fn) {
const blob = new Blob([`(${fn.toString()})()`], { type: 'text/javascript' });
return URL.createObjectURL(blob);
}
const worker = new Worker('./worker.js');
worker.onmessage = function(e) {
const data = e.data;
console.log('Message received from worker');
}
worker.postMessage({ type: 'start', payload: { msg: 'send msg to worker' } }); // 发送信息给
worker.terminate(); //终止 worker
//worker.js
importScripts('foo.js'); //引入脚本与库
self.onmessage = function(e) {
const data = e.data;
console.log('Message received from main script');
postMessage({ type: 'connect', payload: { msg: 'send msg to main' } });
};
self.close();
- SharedWorker:共享 worker 可以同时被多个脚本使用(window, iframes 或者甚至是多个worker)。
//main.js
const myWorker = new SharedWorker('worker.js');
myWorker.port.onmessage = function(e) {
const data = e.data;
console.log('Message received from worker');
}
//worker.js
onconnect = function(e) {
const port = e.ports[0];
port.onmessage = function(e) {
const data = e.data;
port.postMessage(workerResult);
}
}
- ServiceWorker:控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。
//app.js
const registerServiceWorker = async () => {
if ("serviceWorker" in navigator) {
try {
const registration = await navigator.serviceWorker.register("/sw.js", {
scope: "/",
});
if (registration.installing) {
console.log("正在安装 Service worker");
} else if (registration.waiting) {
console.log("已安装 Service worker installed");
} else if (registration.active) {
console.log("激活 Service worker");
}
} catch (error) {
console.error(`注册失败:${error}`);
}
}
};
registerServiceWorker();
//sw.js
const addResourcesToCache = async (resources) => {
const cache = await caches.open("v1");
await cache.addAll(resources);
};
self.addEventListener("install", (event) => {
event.waitUntil(
addResourcesToCache([
"/",
"/index.html",
"/style.css",
"/app.js",
"/image-list.js",
"/star-wars-logo.jpg",
"/gallery/bountyHunters.jpg",
"/gallery/myLittleVader.jpg",
"/gallery/snowTroopers.jpg",
])
);
});
const putInCache = async (request, response) => {
const cache = await caches.open("v1");
await cache.put(request, response);
};
const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
// 首先,尝试从缓存中获取资源
const responseFromCache = await caches.match(request);
if (responseFromCache) {
return responseFromCache;
}
// 然后尝试从网络中获取资源
try {
const responseFromNetwork = await fetch(request);
// 响应可能会被使用
// 我们需要将它的拷贝放入缓存
// 然后再返回该响应
putInCache(request, responseFromNetwork.clone());
return responseFromNetwork;
} catch (error) {
const fallbackResponse = await caches.match(fallbackUrl);
if (fallbackResponse) {
return fallbackResponse;
}
// 当回落的响应也不可用时,
// 我们便无能为力了,但我们始终需要
// 返回 Response 对象
return new Response("Network error happened", {
status: 408,
headers: { "Content-Type": "text/plain" },
});
}
};
self.addEventListener("fetch", (event) => {
event.respondWith(
cacheFirst({
request: event.request,
fallbackUrl: "/gallery/myLittleVader.jpg",
})
);
});
微前端
微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。
qiankun
- JS沙箱:把
js
代码包裹了一层function
,然后再把内部的window
用Proxy
包一层,这样内部的代码就被完全隔离了。 - CSS隔离:提供了两种样式隔离方案shadow dom(外界和内部完全隔离)和自己实现的scoped(类似Vue的scoped css)。
- 存在的问题:挂在
body
的弹窗样式设置不上
wujie
- JS沙箱:将子应用的
js
放置在iframe
中运行,实现了应用之间window
、document
、location
、history
的完全解耦和隔离。 - CSS隔离:将子应用的
dom
放置在webcomponent
+shadowdom
的容器中,除了可继承的css
属性外实现了应用之间css
的原生隔离。
BFC
BFC
是一个完全独立的空间(布局环境),让空间里的子元素不会影响到外面的布局。
BFC规则
BFC
就是一个块级元素,块级元素会在垂直方向一个接一个的排列BFC
就是页面中的一个隔离的独立容器,容器里的标签不会影响到外部标签- 每个元素的
margin
的左边, 与包含块border
的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此 BFC
的区域不会与float
元素重叠- 垂直方向的距离由margin决定, 属于同一个
BFC
的两个相邻的标签外边距会发生重叠 - 计算
BFC
的高度时,浮动元素也参与计算
触发BFC
- 根元素或其它包含它的元素
- 浮动
float: left/right/inherit
- 绝对定位元素
position: absolute/fixed
- 行内块
display: inline-block
- 表格单元格
display: table-cell
- 表格标题
display: table-caption
- 溢出元素
overflow: hidden/scroll/auto/inherit
- 弹性盒子
display: flex/inline-flex
- display: flow-root(显示创建
BFC
)
BFC应用
- 解决块级元素垂直方向
margin
重叠 - 解决浮动元素高度塌陷问题
- 清除浮动
Babel
@babel/core
:是babel
的核心,主要起串联的作用,功能包括加载配置、调用@babel/parser
解析AST、调用@babel/traverse
遍历并操作AST、调用@babel/generator
生成代码。@babel/parser
:将源代码解析为AST。(JSX,Typescript,Flow以及最新的ECMAScript规范)@babel/traverse
:对AST进行深度优先遍历,转换插件会通过它获取感兴趣的AST节点,对节点继续操作。@babel/generator
:将AST转换为源代码,支持SourceMap。@babel/types
:AST节点构造器和断言。@babel/template
:模板引擎,可以将字符串代码转换为AST。
Webpack
graph LR
解析入口文件 --> 加载插件 --> 加载loader --> 构建依赖树 --> 文件写入 --> 监听文件变化;
监听文件变化 -- 重新编译 --> 解析入口文件;
- loader
- pre: 前置loader
- normal: 普通loader
- inline: 行内loader
- post: 后置loader
Web安全
XSS
XSS (Cross-Site Scripting),跨站脚本攻击是指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击。
- 反射型:非持久型 XSS 漏洞,一般是通过给别人发送带有恶意脚本代码参数的 URL,当 URL 地址被打开时,特有的恶意代码参数被 HTML 解析、执行。
- 存储型:持久型 XSS 漏洞,一般存在于 Form 表单提交等交互功能,如文章留言,提交文本信息等,黑客利用的 XSS 漏洞,将内容经正常功能提交进入数据库持久保存,当前端页面获得后端从数据库中读出的注入代码时,恰好将其渲染执行。
防御措施:
- CSP:
Content-Security-Policy
内容安全策略,CSP本质上就是建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。 - 转义字符:用户的输入永远不可信任的,最普遍的做法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义。
- HttpOnly:这是预防XSS攻击窃取用户cookie最有效的防御手段。
CSRF
CSRF(Cross Site Request Forgery),即跨站请求伪造,是一种常见的Web攻击,它利用用户已登录的身份,在用户毫不知情的情况下,以用户的名义完成非法操作。
防御措施:
- SameSite:可以对 Cookie 设置 SameSite 属性。
- Referer:通过检查http包头referer的值是不是这个页面,来判断是不是CSRF攻击。
- CSRF Token:发送请求时在HTTP 请求中以参数的形式加入一个随机产生的token,并在服务器建立一个拦截器来验证这个token。
- 验证码:应用程序和用户进行交互过程中,特别是账户交易这种核心步骤,强制用户输入验证码,才能完成最终请求。
点击劫持
点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击。
防御措施:
- X-FRAME-OPTIONS:
X-Frame-Options
响应头是用来给浏览器指示允许一个页面可否在<frame>
、<iframe>
、<embed>
或者<object>
中展现的标记。 - JavaScript防御:当通过 iframe 的方式加载页面时,攻击者的网页直接不显示所有内容(self!==top)。
URL跳转漏洞
其原理是黑客构建恶意链接(链接需要进行伪装,尽可能迷惑),发在QQ群或者是浏览量多的贴吧/论坛中。安全意识低的用户点击后,经过服务器或者浏览器解析后,跳到恶意的网站中。
防御措施:
- referer限制:如果确定传递URL参数进入的来源,我们可以通过该方式实现安全限制,保证该URL的有效性,避免恶意用户自己生成跳转链接。
- Token验证:通过在生成的链接里加入用户不可控的Token对生成的链接进行校验,可以避免用户生成自己的恶意链接从而被利用。
SQL注入
SQL注入是一种常见的Web安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击。
防御措施:
- 严格限制Web应用的数据库的操作权限
- 后端代码检查输入的数据是否符合预期
- 对进入数据库的特殊字符(',",\,<,>,&,*,; 等)进行转义处理,或编码转换
- 所有的查询语句建议使用数据库提供的参数化查询接口(即不要直接拼接SQL语句)
OS命令注入攻击
OS命令注入攻击指通过Web应用,执行非法的操作系统命令达到攻击的目的。只要在能调用Shell函数的地方就有存在被攻击的风险。倘若调用Shell时存在疏漏,就可以执行插入的非法命令。
防御措施:
- 后端对前端提交内容进行规则限制(比如正则表达式)
- 在调用系统命令前对所有传入参数进行命令行参数转义过滤
- 不要直接拼接命令语句,借助一些工具做拼接、转义预处理