目标
- 网页加载过程
- 常见的性能优化
- 基本的前端安全预防
JS运行环境
- 浏览器(server端有Node.js)
- APP内嵌Web View
- 例如 微信内嵌浏览器
网页如何加载并渲染出来的
问题:从输入URL到渲染出页面的整个过程 加载过程
- 浏览器 DNS解析: 通过域名 找到 IP
- 浏览器 通过IP地址找到发送HTTP请求
- 服务端处理HTTP请求,并返回浏览器 渲染过程
- 根据HTML生成
DOM Tree
- 根据
CSS
生成CSSOM
- 将
DOM Tree
和CSSOM
整合为Render Tree
- 当遇到
<script>
时 停止渲染(因为script
中可能会修改Render Tree,渲染没有意义),优先加载并执行JS代码,完成再继续 - 直到Render Tree渲染完成
CSS 放在head前目的
- 避免重复渲染
- 如果放在HTML下面,HTML渲染完后,会加载CSS,假如发现字体不同,会改变字体,导致重复渲染,同时用户可以看到字体变化(不友好)
JS放在最后的目的
- JS可能会阻断渲染进程,使得页面渲染时间过长
图片加载会暂停渲染过程吗? 不会
- 因为图片不会改变DOM结构,加载完最多会重排一下
document.addEventListener('DOMContentLoaded', function() {
// DOM 渲染完即可执行,此时图片、视频还可能没有加载完
})
window.addEventListener('load', function() {
// 页面的全部资源加载完才会执行,包括图片、视频等
})
性能优化
角度
- 加载更快
- 渲染更快 优化原则
- 多使用内存、缓存等方法(用空间换时间)
- 减少CPU 计算量,减少网络加载耗时 实现
- 加载更快
- 减少资源体积:压缩代码
- 减少访问次数:合并代码,SSR服务器端渲染,使用缓存
- 使用更快的网络:CDN(静态 static.com)
- 让渲染更快
- CSS放在head里,JS放在body最下面
- 尽早开始执行JS,用DOMContentLoaded触发
- 懒加载(图片懒加载,上滑加载更多)
- 对DOM查询进行缓存
- 频繁操作DOM操作,合并到一起插入ODM结构
- 节流throttle 防抖debounce 例子
- 缓存
- webpack打包添加hash的时候,为了通过hash判断文件是否有变化,如果没有变化,命中304,不用重复下载文件
- hash是根据文件内容计算的,如果文件内容不变,hash不变,url不变
- url和文件不变,自动触发http缓存机制,返回304
- webpack打包添加hash的时候,为了通过hash判断文件是否有变化,如果没有变化,命中304,不用重复下载文件
- SSR(服务端渲染:将网页和数据一起加载,一起渲染)
- 非SSR: 前后端分离 先渲染页面 再加载数据 在渲染数据
- SSR:JSP ASP PHP
防抖 debounce
应用场景
- 输入框 不停输入,前后输入间隔大于某个时间,才触发
- 按钮 多次点击 最后一次才触发
<input id='input1' />
function debounce(fn, delay) {
let timer;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
fn().apply(this, arguments);
}, delay);
};
}
document.getElementById("input1").addEventListener("keyup", debounce(function () {
console.log('keyup')
}, 500));
节流 throttle
无论触发多快,都会每隔一定时间触发一次
应用场景
- 拖拽元素,获取元素的位置
- 窗口resize
const throttle = function (fn, delay = 100) {
let timer = null;
return function () {
if (timer) {
return;
}
timer = setTimeout(() => {
// 不可以用function
fn.apply(this, arguments);
timer = null;
}, delay);
};
};
// document.getElementById("dragDiv").addEventListener("drag", function (e) { console.log(e.offsetY, e.offsetX); });
document.getElementById("dragDiv").addEventListener(
"drag",
throttle(function (e) {
// 不可以用箭頭函數,因为e是传给throttle返回的函数的,如果没有fn.apply,e.offsetY会报错 ’offsetY‘ of undefined
console.log(e.offsetY, e.offsetX);
}, 200)
);
WEB安全
xss攻击
什么是xss攻击
- xss(Cross-Site Scripting)跨站脚本攻击,是一种代码注入攻击。
- 通过脚本,获取Cookie,sessionId等敏感信息
- 分类
- 存储型
- 放射型
- DOM型
预防
- 需要前端后端同时处理
- 后端预防 存储型和反射型攻击
- 前端预防 DOM型攻击
DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。
在使用
.innerHTML
、.outerHTML
、document.write()
时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用.textContent
、.setAttribute()
等。如果用 Vue/React 技术栈,并且不使用
v-html
/dangerouslySetInnerHTML
功能,就在前端 render 阶段避免innerHTML
、outerHTML
的 XSS 隐患。DOM 中的
内联事件监听器
,如location
、onclick
、onerror
、onload
、onmouseover
等,<a>
标签的href
属性,JavaScript 的eval()
、setTimeout()
、setInterval()
等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。// 1. 链接内包含恶意代码 <a href="恶意代码" /> // 2. setTimeout/setInterval中调用恶意代码 // 3. location 调用恶意代码 location.href = '恶意代码' // 4. eval() 中调用恶意代码 eval(‘恶意代码’) // 5. 内联事件监听器 中包含恶意代码
- npm xss工具:www.npmjs.com/package/xss
其他安全措施
- HTTP-only Cookie: 禁止JavaScript读取某些敏感Cookie
- 验证码:防止脚本冒充用户提交危险操作
参考:tech.meituan.com/2018/09/27/…
CSRF (Cross-site request forgery)跨站请求伪造
什么是CSRF
攻击者冒充用户对被攻击的网站执行某项操作,例如 转账。
防护策略
CSRF的特点
- CSRF(通常)发生在第三方域名
- CSRF攻击者不能获取Cookie等信息,只是使用
根据上面特点制定策略
- 阻止不明外域的访问
- 同源检测
- Samesite Cookie
- 提交时要求俯角本域才能获取的信息
- CSRF Token
- 双重Cookie验证