16-运行环境、安全、性能

237 阅读3分钟

运行环境

  • 运行环境即浏览器(server端有nodejs)
  • 下载网页代码,渲染出页面,期间会执行若干JS
  • 要保证代码在浏览器中:稳定且高效

要知道的知识点

  • 网页加载过程
  • 性能优化
  • 安全

面试相关题目

  • 从输入url到渲染出页面的整个过程
  • window.onload和DOMContentLoaded的区别

知识点及讲解

  • 加载资源的形式

    • html代码
    • 媒体文件,如图片,视频等
    • javascript css
  • 加载资源的过程

    • DNS解析:域名-->IP地址
    • 浏览器根据IP地址向服务器发起http请求
    • 服务器处理http请求,并返回给浏览器
  • 渲染页面的过程

    • 根据HTML代码生成DOM Tree
    • 根据CSS代码生成CSSOM
    • 将DOM Tree 和CSSOM整合形成Render Tree
    • 根据Render Tree渲染页面
    • 遇到<script>则暂时渲染,优先加载执行JS代码,完成再继续
    • 直至把Render Tree渲染完成

建议把css放到<head>中;

建议把JS放到<body>最后;

window.addEventListener('load',function(){
    //页面的全部资源加载完才会执行,包括图片,视频等
})

document.addEventListener('DOMContentLoaded',function(){
    //DOM 渲染完即可执行,此时图片,视频还可能没有加载完
})

题解:

  • 从输入url到渲染出页面的整个过程
    • 下载资源:各个资源类型,下载过程
    • 渲染页面:结合html css javascript图片等
    • 结合上面的知识点讲解结合回答
  • window.onload和DOMContentLoaded的区别
    • load: 页面的全部资源加载完才会执行,包括图片,视频等

    • DOMContentLoaded: DOM 渲染完即可执行,此时图片,视频还可能没有加载完

性能优化(防抖 节流)

性能优化原则

  • 多使用内存、缓存或其他方法
  • 减少CPU计算量,减少网络加载耗时
  • (适用于所有编程的性能优化---空间换时间) 从何入手?:
  • 让加载更快
    • 减少资源体积:压缩代码
    • 减少访问次数:合并代码,SSR服务器端渲染,缓存
    • 使用更快的网络:CDN
  • 让渲染更快
    • css放到<head>中;JS放到<body>最下面
    • 尽早开始执行JS,用DOMContentLoaded触发
    • 懒加载(图片懒加载,上滑加载更多)
    • 对DOM查询进行缓存
    • 频繁DOM操作,合并到一起插入DOM结构
    • 节流throttle,防抖debounce

面试相关题目

  • 手写防抖、手写节流

代码示例

资源合并

<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>

三合一变成一个文件,减少网络请求
<script src="abc.js"></script>

缓存

  • 静态资源加hash后缀,根据文件内容计算hash
  • 文件内容不变,则hash不变,则url不变
  • url和文件不变,则会自动触发http缓存机制,返回304
module.exports = {
    mode:'production',
    entry:path.join(__dirname,'src','index'),
    output:{
        filename:'bundle.[contenthash].js',
        path:path.join(__dirname,'dist')
    }
}

16-6 前端性能优化的示例[00-00-55][20210625-140202987].jpg

CDN

16-6 前端性能优化的示例[00-04-27][20210625-14061864].jpg

SSR

  • 服务器端渲染:将网页和数据一起加载,一起渲染
  • 非SSR(前后端分离):先加载网页,再加载数据,再渲染
  • 早先的JSP ASP PHP,现在的vue React SSR

懒加载

把图片先用个预览图片代替,当图片露出屏幕时再真正加载

    <img id="img1" src="preview.png" data-realsrc="abc.png" />
    <srcipt type="text/javascript">
        var img1 = document.getElementById('img1');
        img1.src = img1.getAttribute('data-realsrc');
    </script>

缓存DOM查询

//不缓存DOM查询结果
for(let i = 0; i < document.getElementsByTagName('p').length;i++){
    //每次循环,都会计算length,频繁进行DOM查询
}

//缓存DOM查询结果
const pList = document.getElementsByTagName('p')
const length = pList.length;
for(let i = 0; i < length; i++){
    //缓存length,只进行一次DOM查询
}

多个DOM操作一起插入到DOM结构

const listNode = document.getElementById('list')

//创建一个文档片段,此时还没有插入到DOM树中
const frag = document.createDocumentFragment()

//执行插入
for(let x = 0; x < 10; x++){
    const li = document.createElement("li"); 
    li.innerHTML = "List item" + x;
    frag.appendChild(li);
}

//都完成之后,再插入到DOM树中
listNode.appendChild(frag);

尽早开始执行JS

window.addEventListener('load',function(){
    //页面的全部资源加载完才会执行,包括图片,视频等
})
document.addEventListener('DOMContentLoad',function(){
    //DOM渲染完即可执行,此时图片、视频还可能没有加载完
})

防抖debounce

  • 监听一个输入框的,文字变化后触发change事件
  • 直接用keyup事件,则会频繁触发change事件
  • 防抖:用户输入结束或暂停时,才会触发change事件

在实践经验中,因为没用防抖,导致用复杂正则较难输入框快速输入内容时卡壳

文件index.html

<!DICTYPE html>
<html lang="en">
<body>
	<input type="text" id="input1">
	<script src="./debounce.js"></script>
</body>

</html>

文件debounde.js

const input1 = document.getElementById('input1')
let timer = null;

/*input1.addEventListener('keyup',function(){
	if(timer){
		clearTimeout(timer);
	}
	timer = setTimeout(()=>{
		//模拟触发change事件
		console.log(input1.value);
		//清空定时器
		timer = null;
	},500)
	
})*/

//封装
function debounce(fn, delay = 500){
		//timer 是闭包中的
	let timer = null;
	return function(){
		if(timer){
			clearTimeout(timer);
		}
		timer = setTimeout(()=>{
			fn.apply(this,arguments);
			timer = null;
		},delay)
		
	}
	
}

input1.addEventListener('keyup',debounce(function(){
	console.log(input1.value);
},600))

节流throttle

  • 拖拽一个元素时,要随时拿到该元素被拖拽的位置
  • 直接用drag事件,则会频繁触发,很容易导致卡顿
  • 节流:无论拖拽速度多快,都会每隔100ms触发一次
const div1 = document.getElementById('div1');
let timer = null;

/*div1.addEventListener('drag',function(e){
	console.log(e.offsetX,e.offsetY);
	if(timer){
		return;
	}
	timer = setTimeout(()=>{
		console.log(e.offsetX,e.offsetY);
		timer = null;
	},100)
})*/

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.offsetX,e.offsetY);
}))

安全

问题:常见的WEB前端攻击方式有哪些?

  • XSS跨站请求攻击
  • XSRF跨站请求伪造

XSS跨站请求攻击

  • 一个博客网站,我发表一篇博客,其中嵌入<script>脚本
  • 脚本内容:获取cookie,发送到我的服务器(服务器配合跨站)
  • 发布这篇博客,有人查看它,我轻松收割访问者的cookie

XSS预防

  • 替换特殊字符,如<变为&lt;;>变为&gt;
  • <script>变成&lt;script&gt;,直接显示,而不会作为脚本执行
  • 前端要替换,后端也要替换,都做总不会有错
~~~
<script>alert(document.cookie);</script>
换成   &lt;script&gt;alert(document.cookie); &lt;/script&gt;
有xss替换工具,npm可下载 https://www.npmjs.com/package/xss
~~~

XSRF跨站请求伪造

XSRF攻击

  • 你正在购物,看中了某个商品,商品ID是100
  • 付费接口是xxx.com/pay?id=100,但是没有任何验证
  • 我是攻击者,我看中了一个商品,ID是200
  • 我向你发送一封电子邮件,邮件标题很吸引人
  • 但邮件正文隐藏着`<img src=xxx.com/pay?id=200 />
  • 你一查看邮件,就帮我购买了id是200的商品

XSRF预防

  • 使用post接口
  • 增加验证,例如密码、短信验证码、指纹等