live-server给我的代码加了什么?

前言

对于前端程序员来说,live-server插件估计都不陌生,想起刚开始学前端的时候,每次改完代码,浏览器直接就能自动更新页面,真的很方便。一次偶然的机会,我在浏览器中检查代码的时候发现,诶?这段代码是哪里来的?

逐层拆解

看着这段似懂非懂的代码,我决定花上几个晚上认真研究一下,不说废话,一起来看代码。

js 复制代码
if ("WebSocket" in window) {
} else {
console.error(
    "Upgrade your browser. This Browser is NOT supported WebSocket for Live-Reloading."
);}

先来看最外层,这个好理解,判断浏览器的全局对象window是否包含WebSocket;简单来说,WebSocket是浏览器和服务器进行双向即时通讯的一项技术,如果浏览器没有这个对象,就要报错了。

接下来作者用了一个立即执行的匿名函数,作者在代码中使用了var关键字;我们都知道,在函数中用var定义的变量是不会影响到函数以外的变量的,这样就很好地避免污染全局变量。

分析refreshCSS

随后作者定义了一个refreshCSS方法,见名知意,这是用来刷新css样式的。这里要注意一下[].slice.call(),call能够让slice方法的this指向传入的伪数组,对于slice方法来说,不传递参数的时候它可以返回原数组,那么通过这个方法我们得到了一个数组sheets,于此同时,我们还获取了一个parentElement。

js 复制代码
function refreshCSS() {
    //使用[].slice.call()来将获取到所有的link标签塞到一个数组里面
    var sheets = [].slice.call(document.getElementsByTagName("link"));
    //获取第一个head标签
    var head = document.getElementsByTagName("head")[0];
    for (var i = 0; i < sheets.length; ++i) {
        var elem = sheets[i];
        //这是考虑到了有些浏览器可能不支持parentElement的情况
        var parent = elem.parentElement || head;
        //获取一个移除了link本身的父元素
        parent.removeChild(elem);
        var rel = elem.rel;
        if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
            var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, '');
            elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());
        }
        parent.appendChild(elem);
    }
}

这里的代码可能比较晦涩难懂,总的来说,它的含义是先判断其是否是个样式表,然后通过修改文件名来获取最新样式表的。

js 复制代码
var rel = elem.rel; 
if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") { 
    //移除掉原来的缓存参数
    var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, '');
    //使用时间戳替换成最新的缓存参数
    elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf()); 
} 
//将更新了href属性的link标签重新放入该元素
parent.appendChild(elem);

我详细讲一下为什么live-server要给css文件加上一个缓存参数,在正常情况下,浏览器为了节省请求频率,可能会使用本地的文件缓存,如果文件名没有改变,浏览器就会继续使用缓存下来的文件,举个例子,我引入一个link标签,刚开始浏览器页面是这样的。

但是当我改变了test.css文件内容之后,live-server就给它自动加上缓存参数了,我们可以明白这个__cacheOverride就是为了防止浏览器复用之前的样式表的,这样,浏览器看到文件名改变后,就会重新请求最新的样式表,因此,每一次样式表的改变,都对应着独一无二的时间戳。

除此之外,样式表的名字还可能长这样test.css?t=1&_cacheOverride=1704557378563因此,作者需要通过正则(&|\?)判断其是否是第一个参数,而下面的url.indexOf('?') >= 0 ? '&' : '?'也是同理。

通过执行refreshCSS方法,我们可以获取到一个最新的样式表。

建立WebSocket链接

接下来,创建一个新的websocket链接,作者做了一个简单的判断,如果协议名是http:,那么就使用 ws://协议,否则就使用更安全的wss://协议。他的最终请求地址是这样的ws://127.0.0.1:5500/test2.html/ws

在这里,当服务器端文件发生了变化后,服务器会判断样式表是否发生了变化,如果是样式表发生了变化,则返回消息msg.data == 'refreshcss',浏览器因为link标签href的变化再次请求了test.css,如果是页面发生了变化,则返回reload,浏览器直接刷新页面重新请求就好了。

js 复制代码
var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';
var address = protocol + window.location.host + window.location.pathname + '/ws';
var socket = new WebSocket(address);
socket.onmessage = function (msg) {
    if (msg.data == 'reload') window.location.reload();
    else if (msg.data == 'refreshcss') refreshCSS();
};

这是我修改了样式表之后的截图。

最后,这段代码就比较简单了,浏览器执行到这里,表明已经启用热重载了, 但是要告知用户一下,于是就在sessionStorage插了一个flag,并打印console.log('Live reload enabled.');

js 复制代码
if (sessionStorage && !sessionStorage.getItem('IsThisFirstTime_Log_From_LiveServer')) {
    console.log('Live reload enabled.');
    sessionStorage.setItem('IsThisFirstTime_Log_From_LiveServer', true);
}

总结

讲了这么多,估计脑袋也晕晕的(@_@;),那咱们再总结一下:

  1. 我们在使用vscode的live-server插件打开文件的时候,它会自动帮我们创建一个端口号为5500的服务器,同时对我们的html文件注入一段代码。
  2. 我们的浏览器接收到文件后就会与服务器建立websocket连接,同时会在在控制台打印一下Live reload enabled。
  3. 当我们修改代码的时候,服务器会判断我们修改的是否是样式表,如果修改了样式表,就会通过websocket告诉我们refreshCSS,如果改变的是其他的文件(包括js文件),服务器就会返回reload。
  4. 浏览器在收到websocket请求以后,判断msg.data如果reload,则刷新浏览器重新请求数据,如果是refreshCSS,则更改css文件的缓存参数来重新请求样式表。

最后,全部代码在这里~

js 复制代码
	// <![CDATA[  <-- For SVG support
	if ('WebSocket' in window) {
		(function () {
			function refreshCSS() {
				var sheets = [].slice.call(document.getElementsByTagName("link"));
				var head = document.getElementsByTagName("head")[0];
				for (var i = 0; i < sheets.length; ++i) {
					var elem = sheets[i];
					var parent = elem.parentElement || head;
					parent.removeChild(elem);
					var rel = elem.rel;
					if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
						var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, '');
						elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());
					}
					parent.appendChild(elem);
				}
			}
			var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';
			var address = protocol + window.location.host + window.location.pathname + '/ws';
			var socket = new WebSocket(address);
			socket.onmessage = function (msg) {
				if (msg.data == 'reload') window.location.reload();
				else if (msg.data == 'refreshcss') refreshCSS();
			};
			if (sessionStorage && !sessionStorage.getItem('IsThisFirstTime_Log_From_LiveServer')) {
				console.log('Live reload enabled.');
				sessionStorage.setItem('IsThisFirstTime_Log_From_LiveServer', true);
			}
		})();
	}
	else {
		console.error('Upgrade your browser. This Browser is NOT supported WebSocket for Live-Reloading.');
	}
	// ]]>
相关推荐
前端拾光者35 分钟前
利用D3.js实现数据可视化的简单示例
开发语言·javascript·信息可视化
木子02041 小时前
前端VUE项目启动方式
前端·javascript·vue.js
endingCode2 小时前
45.坑王驾到第九期:Mac安装typescript后tsc命令无效的问题
javascript·macos·typescript
Myli_ing3 小时前
HTML的自动定义倒计时,这个配色存一下
前端·javascript·html
I_Am_Me_3 小时前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
℘团子এ3 小时前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z3 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁4 小时前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜4 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish4 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue