运行环境即浏览器(server端有nodejs)
过程:下载网页代码,渲染出页面,期间回执行若干JS
保证代码在浏览器中:稳定且高效
-
网页加载过程
- 加载资源的形式
- html代码
- 媒体文件、如图片、视频
- js、css
- 加载资源的过程
- DNS解析:域名 -> IP地址
(域名识别性高,如www.baidu.com,ip地址很难记,大型网站,不同的区域IP地址不一致,他们做了代理,DNS会根据地域解析出不同的IP,让用户访问更快) - 浏览器根据IP地址向服务器发起http请求
(浏览器只是发起方,真正核心模块还是操作系统的能发生网络服务的系统服务,http还涵盖三次握手) - 服务器处理http请求,并返回给浏览器
- DNS解析:域名 -> IP地址
- 渲染页面的过程
- 根据HTML生成DOM tree
- 根据CSS生成CSSOM
- 根据DOM tree和CSSOM整合形成Render tree
- 根据Render tree渲染页面
- 遇到<script>则暂停渲染,优先加载并执行JS代码,完成再继续
- css放在head中:是希望在渲染前就能完成CSSOM的生成,这样避免render tree要重新渲染
- js放在body最后:因为js会阻塞render,延长页面加载时间,导致用户体验变差
- 图片不会阻塞render,它只会在资源请求完成后把资源插进来,只可能导致重排
- onload事件:所有资源加载完执行,包括图片和视频
- DOMContentLoaded 渲染完成即执行,此刻图片可能没加载完(更推荐,因为js对dom操作可能与资源无关)
(js进程和渲染进程是共用一个线程,因此会出现堵塞,因为js有可能会改变render tree的结构) - 直至把Render trre渲染完成
- 加载资源的形式
-
性能(和体验)优化(全面的方案:只需要讲点,不需要展开)
- 原则
- 多使用内存、缓存和其他方法
- 减少CPU计算量,减少网络加载耗时
- 空间换时间
- 如何入手
- 加载更快
- 减少资源体积:压缩代码和图片
- 减少访问次数:合并代码、SSR服务器端渲染、缓存
- http的缓存机制(浏览器和服务器都遵循):
- 静态资源加hash后缀,根据文件内容计算hash
- 文件内容不变,则hash不变,则url不变
- url和文件不变,则会自动触发http缓存机制,返回304
- SSR
- 服务端渲染:将网页和数据一起加载,一起渲染
- 非SSR(前后端分离):先加载网页,再加载数据,再渲染数据
- http的缓存机制(浏览器和服务器都遵循):
- 使用更快的网络:CDN(根据区域做服务区处理)
- 渲染更快
- ccs放在head,js放在body下面
- 尽早开始执行js,用DOMContentLoaded触发,而不是onload
- 懒加载(图片懒加载,上滑加载更多)
- 对DOM查询进行缓存
- 频繁DOM操作,合并到一起插入DOM结构(frag)
- 防抖debounce,节流throttle(渲染更流畅,是一种体验更佳的方案)
- 防抖:其实还是在一直执行事件,只是设置一个setTimeout来设置一个时间间隔,如果你在时间间隔内又触发事件,则之前的setTimeout被清空,如果超出,则执行setTimeout里面的代码
- 加载更快
- 原则
function debounce(fn,delay = 500){
// timer 放在函数内形成闭包,不被外界所修改
let timer = null
// 如果距离上一次keyup的事件在500ms以内,则上一次的console.log(e.target.value)所在的setTimeout会被清楚;如果超出,则会执行setTimeout里面的代码
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,arguments)
clearTimeout(timer)
},delay)
}
}
input.addEventListener('keyup',debounce(function(e){
console.log(e.target.value)
},700))
* 节流:比如拖拽事件的进行,如果期间有修改dom的操作,一直执行消耗性能大,会导致最终出现屏幕卡屏的现象,因此设置为,如果没超过100ms,就return
出去,如果超出了,就设置执行操作
function throttle(fn,delay=100){
let timer = null
return function(){
if(timer){
return
}
timer = setTimeout(()=>{
fn.apply(this,arguments)
timer = null
}, delay)
}
}
div1.addEventListener('drag',throttle(function(e){
console.log(e)
},200))
* 补充:
* 两个封装函数的apply作用:在addEventListener的第二个参数写入throttle或者debounce,那么事件对象e就会传为他们封装的renturn函数中,因此arguments实则是e
* setTimeout一定要是箭头函数,这样才不会改变this的指向,this要传入fn当中的
- 安全
- XSS跨域请求攻击
- 原理:在发布内容中插入script,获取客户端用户的cookie,配合服务器跨域,接受ajax发出的请求
- 解决办法:
- 替换特殊字符,
<替换<``>替换>,这样script会被直接显示,而不是当作脚本 - 前后端都要做替换,这样比较安全
- 可以直接用npm中有xxs工具
- 替换特殊字符,
- XSS跨域请求攻击
- XSRF跨域请求伪造
- 场景
- 用户登录淘宝平台(用户已登录)
- 向用户发送一封邮件,里面包含一个
<img src="xxx.pay?id=200"> - 就可以盗取用户信息进行购买id为200的商品
- 预防:
- post接口(post接口做跨域需要server的配合)
- 增加验证,如密码、短信验证码、指纹