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.');
	}
	// ]]>
相关推荐
CodeClimb5 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
光头程序员7 小时前
grid 布局react组件可以循数据自定义渲染某个数据 ,或插入某些数据在某个索引下
javascript·react.js·ecmascript
fmdpenny8 小时前
Vue3初学之商品的增,删,改功能
开发语言·javascript·vue.js
小美的打工日记8 小时前
ES6+新特性,var、let 和 const 的区别
前端·javascript·es6
涔溪9 小时前
有哪些常见的 Vue 错误?
前端·javascript·vue.js
程序猿online9 小时前
前端jquery 实现文本框输入出现自动补全提示功能
前端·javascript·jquery
Turtle11 小时前
SPA路由的实现原理
前端·javascript
HsuYang11 小时前
Vite源码学习(九)——DEV流程中的核心类(下)
前端·javascript·架构
傻小胖11 小时前
React 中hooks之useInsertionEffect用法总结
前端·javascript·react.js
蓝冰凌13 小时前
【整理】js逆向工程
javascript·js逆向