。
此文章会持续更新,我会将自己平常学习到的性能优化方案写在此文章中,欢迎各位补充以及指出错误👏🚀🚀
HTML层级的优化
HTML的结构层在"前端三剑客"中是最基础的,也是相对较为简单的,同时还是最容易被忽视的,然而这一层在很多时候却很重要,一些不好的习惯或者一次误操作,可能会在不知不觉中是系统产生额外开销,降低用户体验。
标签的属性以及模板引擎
化繁为简
无招胜有招才是最高明的做法,一些花里胡哨的做法可能初看会眼前一亮,但实际不利于后续的使用,假如代卖是一款产品,那么写代码的人和后续的维护人员都是这款产品的用户,如果代码写的简单,后续维护起来会特别方便。
对于浏览器而言,一个网页的.html文件就是某种格式的数据,字符串而已。
减少HTML层级嵌套。
浏览器在解析HTML过程,创建DOM树的过程中,基本会循环下面三个步骤
- (1)遇到字符<时,状态更改为"标记打开状态"。
- (2)当接收一个a~z字符时,会创建"起始标记",状态更改为"标记名称状态",并保持状态到接收>字符时。此期间的字符串会形成一个新的标记名称。接收到>字符后,将当前的新标记发送给树构造器,状态改回"数据状态"。
- (3)当接收下一个输入字符/时,会创建关闭标记、打开状态,并更改为"标记名称状态"。当接收>字符时,会将当前的新标记发送给树构造器,并改回数据状态。
浏览器在创建解析器的同时,也会创建document对 document对象象。在树构建阶段,以document为根节点的DOM树会不断进行修改,添加各种元素。标记生成器发送的每个节点都会由树构建器进行处理。 每个标记都有对应的DOM元素,这些元素会在接收标记时创建。我们可以在浏览器中打印出window,找到document对象,当我们写的标签多一层时,整个对象就会多一层,同时也多出很多属性和值。
也就是说,浏览器在解析HTML 文件并构建DOM树的过程中,会将我们写的标签向DOM树中挂载,层级越深,DOM树就越深。DOM树在实际的访问中是需要遍历的,
我们学算法的时候也知道时间复杂度吧,一层for循环大多是O(n)的,而两层可能就O(n2)了,一样的道理,如果你的HTML层级过深,遍历的时间复杂度可想而知😂
总结
所以:无论是通过传统的方式,还是MVVM框架自定义模板组件的方式,层级嵌套都不宜过深,如果组件中组件,甚至组件中组件的组成还是组件,就可以从业务上重新考虑是否需要细化。组件的嵌套不宜过深最好不要超过3层,否则可能会带来另一个问题,即父子组件间通信的代价。
减少空标签,无用标签的滥用
HTML的所有标签元素其实都是一个长方形,如果想要做成平行四边形效果,就要用到三个标签(1主2空), 其中两个标签实际上是空的,只是提供一个三角形的样式展示,除此之外,没有其他任何的作用。
此时的HTML结构中就因为做这个效果而多了两个空标签,这两个空标签不但没有必要,甚至还干扰后续开发维护人员。 那比较推荐的做法是什么呢?就是定义一个梯形类,通过:before、:after伪类完成两个空标签的功能。比如 HTML 可能只需要:什么意思
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>平行四边形示例</title>
<style>
/* 不推荐做法的样式设置(先注释掉,方便对比) */
/*
.parallelogram-container {
width: 200px;
height: 100px;
position: relative;
}
.empty-triangle-top {
width: 0;
height: 0;
border-bottom: 50px solid transparent;
border-right: 50px solid lightblue;
border-top: 50px solid transparent;
position: absolute;
top: 0;
left: 0;
}
.parallelogram-main {
width: 200px;
height: 100px;
background-color: lightblue;
color: white;
text-align: center;
line-height: 100px;
position: relative;
z-index: 1;
}
.empty-triangle-bottom {
width: 0;
height: 0;
border-top: 50px solid transparent;
border-left: 50px solid lightblue;
border-bottom: 50px solid transparent;
position: absolute;
bottom: 0;
right: 0;
}
*/
/* 推荐做法的样式设置 */
.parallelogram {
position: relative;
width: 200px;
height: 100px;
/* 其他主体样式设置,比如背景色、文字样式等 */
background-color: lightblue;
color: white;
text-align: center;
line-height: 100px;
}
.parallelogram:before {
content: "";
position: absolute;
top: 0;
left: -50px;
width: 0;
height: 0;
border-top: 50px solid transparent;
border-right: 50px solid lightblue;
border-bottom: 50px solid transparent;
}
.parallegram:after {
content: "";
position: absolute;
bottom: 0;
right: -50px;
width: 0;
height: 0;
border-top: 50px solid transparent;
border-left: 50px solid lightblue;
border-bottom: 50px solid transparent;
}
</style>
</head>
<body>
<!-- 不推荐做法(注释掉,方便对比) -->
<--这两个空标签除了实现这个特定的样式效果外,没有其他实质作用,而且它们存在于 HTML 结构中,会让整个代码结构变得复杂,后续如果开发人员需要修改页面结构、维护代码或者进行样式调整时,还得额外考虑这两个 “多余” 的空标签,容易造成混淆,增加维护成本。-->
<div class="parallelogram-container">
<div class="empty-triangle-top"></div>
<div class="parallelogram-main">这是平行四边形里的内容</div>
<div class="empty-triangle-bottom"></div>
</div>
<!-- 推荐做法 -->
<!-- <div class="parallelogram">这是平行四边形里的内容</div> -->
</body>
</html>
这样运行后就不会产生空标签了,后续人员在复用的时候也会很简单,既提高了性能,又减少了多人协作的沟通成本,何乐而不为呢?
当然还有其他做法,如利用canvas,但 canvas 通常用来画 CSS 画不出来的东西或制作一些较复杂的动画等,用在这里有些"大材小用"。
在实际的开发过程中,空标签、无用标签被用到的机会很少。一些大的互联网公司除了有后端的代码审查之外,还会有前端的代码审查,所以出现这种垃圾代码的概率偏低。如果遇到要用空标签的情况,不妨换一种解决思路。
标签的style属性
你可能会看到这样的HTML:
<div style="text-align: center;
font-size: 30px;
font-weight: bold;
margin-top: 100px;
color: red;">
Hello World
</div>>
与上面的复杂方式对比后,你会毫不犹豫选择下面的方式
<div class="hello">
你好啊
</div>
对于浏览器来说,整个页面的元素在正常情况下都是非常多的。原来几个字符就能搞定的事情现在需要用一大串字符,浏览器要解析的HTML文件可能从原来的几百B变成LKB甚至十几KB或更多,文件大小被翻了好几番,降低了解析的性能,与此同时浏览器还需要判断哪些是标签,哪些是 CSS 样式。如果此时再插入一段 Script 脚本就更复杂了。
通过 Style 属性在 HTML 中插入样式,非常不利于维护,因为一个网页通常都是由统一风格的界面组成的,这样就会有非常多的样式可以被复用。假如采用Style来写样式,那么就不得不重复地写入更多的样式,开发效率极低
总结
所以我们在实际开发中应该将HTML结构与作为样式的CSS分离,而不是在每个标签的位置写入。总体看起来直观明朗,不仅文件体积小,加载性能高,而且后续维护起来也高效,简便。
标签的自定义属性
合理利用模板引擎
无论是JQuery时代的template还是MVVM框架自带的模板语法,又或是ES6提供的模板字符串,都是一种模板引擎。 为什么引用模板引擎会比传统方式快呢?
现有的响应式的 MVVM 框架引擎,如 Vue,React,Angular等,都自带语法模板。利用框架自身所带的语法模板当然是最好的选择,但如果只是为了渲染一些小模板,直接工程化搭建一个项目出来显然有些大材小用了。这时候导人一个几千字节的小文件显然是不二的选择。目前比较常见的模板引擎如下。-
- 百度:baiduTemplate.js。
- 阿里巴巴:juicer.js。
- 腾讯:artTemplate.js。
- 此外还有:doT.js,JQuery作者开发的tmpljs。 不同的模板引擎,对页面的加载速度的优化不同。
模板引擎到底是怎么提升页面性能的呢?
接下来我们大致了解一下其原理及实现过程。它一般需要传入两个值,一个是目标模板的ID,一个是 JSON 数据存放的变量options。原理简化如下。
- (1)通过符合某种特定规则的正则匹配,如(%%),将目标模板片段中的带有这种类型的片段匹配出来。
- (2)将这些字符,替换成传入的数据,重新合成一个完整的字符串,注意此时是字符串,并且字符串里有 JavaScript代码。
- (3)通过 new Function等方式将字符串作为参数传人,并执行这段 JavaScript代码,重新拼合成一个完整的HTML片段。
让我们看下面的代码
<div class='test'>
<ul></ul>
</div>
<script>
var domLi = '<li>' + d.name + '</li>''<li>' +d.age + '</li>' +
'<li>' + d.sex + '</li>';
$('.test ul').append($(domLi));
</script>
用了模板引擎之后,就变成这样:
<div class='test'></div>
<script type='text/dc-tmpl' id = 'dorsey'>
<ul>
<li>{% d.name %}</li>
<li>{% d.age %}</li>
<li>{% d.sex %}</li>
</ul>
</script>
从原理看出,无论模板引擎中的js引擎要计算多久,总会比频繁操作DOM 有些人可能会这样操作:
for(var i in d) {
$(".test ul).append('<li>' + d[i]+ '</li>')
};
上述操作会造成页面频繁地重排、重绘、降低性能。 模板引擎做的事情就是,给它一个模板和一个数据源,他会返回一个字符串,只需把这个字符串以innerHTML 等方式写入页面即可。
只在页面写一串字符串是非常快的,不会造成额外的运行负担。写完字符串,浏览器的Reflow 和 Repaint也只有一次,而且是局部性的,这样页面的性能就有可能提升一些好的模板引擎,还会在内部实现步骤上下大功夫,缩短这部分的时间,让最终的性能更好。
下面是完整示例:
不使用模板引擎
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Without Template Engine Example</title>
</head>
<body>
<ul id="userList"></ul>
<script src="./js/script.js"></script>
</body>
</html>
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 }
];
const userListElement = document.getElementById('userList');
// 循环遍历用户数组,创建并添加列表项到页面中
users.forEach(user => {
const listItem = document.createElement('li');
listItem.textContent = `姓名:${user.name},年龄:${user.age}`;
userListElement.appendChild(listItem);
});
直接通过 JavaScript 操作 DOM 元素,循环遍历用户信息数组,手动创建 HTML 的列表项(<li>标签),然后将其添加到页面中指定的<ul>元素内,以此来实现数据的展示。这种方式在简单场景下可行,但当页面结构复杂、数据量较大或者需要频繁更新页面内容时,代码会变得繁琐且难以维护。
使用模板引擎
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>With Template Engine Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"></script>
</head>
<body>
<ul id="userList"></ul>
<script id="userTemplate" type="text/x-handlebars-template">
{{#each users}}
<li>姓名:{{this.name}},年龄:{{this.age}}</li>
{{/each}}
</script>
<script src="script.js"></script>
</body>
</html>
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 },
];
// 获取模板元素
const source = document.getElementById('userTemplate').innerHTML;
// 编译模板
const template = Handlebars.compile(source);
// 渲染数据到模板,并将结果插入到页面中
const html = template({ users });
const userListElement = document.getElementById('userList');
userListElement.innerHTML = html;
在 JavaScript 代码中,首先获取定义好的模板内容,然后使用 Handlebars 的compile函数编译模板,接着将实际的用户数据传递给编译后的模板进行渲染,最后把渲染得到的 HTML 字符串插入到页面指定的 DOM 元素(这里是id为userList的<ul>元素)内。
使用模板引擎使得代码结构更加清晰,将数据展示的逻辑和页面结构的定义相对分离,尤其在处理复杂页面和大量数据渲染时,更易于代码的编写、维护以及复用,提升了开发效率。
link标签的妙用
HTML很多时候被优化的是细节方面的内容,看似最基础的就最容易被忽略,而有些细节对提升整体的页面性能有很大帮助,甚至可以优化SEO,提高搜索排名。 很多人对link的印象主要停留在外链CSS资源上,比如·
<link rel="stylesheet" href="./css/style.css">
现在CSS3引用的方式由于合并的关系,更多采用了如@import的css用法,较少去看框架编译后的文件,link更被忽视。实际上,在prefetch、preload等预加载技术出现以后。link能做的事远比想象中的多。
相对于@import同步的机制而言,多条link之间是异步加载,相互间并不会有阻塞的问题,浏览器在下载link关联的资源时不会停止对当前文档的处理,如果引用的是CSS文件且非常大时,在一定程度上还是会对渲染造成影响。关于link的属性,这里不详细介绍,各位去看文档就可以了。
prefetch
<link rel="prefetch" href="common.css" as="style">
网站性能的提升决定于缓存,能从缓存中加载资源就不去服务端加载。prefetch的原理实际上就是利用浏览器的空闲时间先下载用户指定需要的内容,然后缓存起来用户下次加载时,实际上是从缓存中加载,此时会发现请求的状态码是304。
prefetch最大的作用不在于当前页面,而在于后续可能会访问的页面上。dns-prefetch其实也是一种预加载,只不过适用于处理DNS解析的
<link rel='dns-prefetch'href='http://www.dorsey.com'>
dns-prefetch更多的是用来提前解析一些域名。在一些大型的导航网站、门户网站上可能有很多的网站链接,而用户可能之前完全没访问过这些链接,也就是说,域名对于客户端来说是完全陌生的。
根据用户的浏览历史或习惯,dns-prefetch会在浏览器空闲时预先将这个域名解析成IP,当用户访问这些页面时会直接跳过DNS解析环节。 其实一些搜索引擎,如google,就有这方面的优化,在上网过程中,可能会发现从搜索引擎搜索栏目进入某一页面要比直接在URL输入地址稍快,这是因为它会根据用户的搜索习惯,提前预加载一些网站的资源,从而达到降低访问时长的目的。
preload
<link rel="preload" href="common.css" as="style">
preload是一项新的Web标准,旨在提高性能,让FE 对加载的控制更加粒度化。(“FE” 通常是 “Front End” 的缩写,即前端)
它让开发者有自定义加载逻辑的能力,免受基于脚本的加载器带来的性能损耗。
那preload用于何地呢?🤔
在实际工作中,你可能需要实现这样一个功能:在页面生命周期的某一刻执行一段可拔插的代码段,这时候你会怎么做?
传统的做法就是将这些代码放进某个 js文件中,再在对应的时间节点用script标签引入,执行完再删除。用script标签引入时有一个问题:需要发一个请求,从服务端下载这个 js文件,而这个过程会产生额外的时间消耗。如果这时候页面中已经有了这段代码,但它没有被执行,是否就可以了呢?答案是否定的,浏览器本身的机制对于script引入的js 资源是加载后执行。
那该怎么做呢?🤔
这就是link的另一个妙用了。通过preload预先加载,这种异步预先加载的资源暂时不会被浏览器用到,也就不会被执行
当你需要执行时,再通过script引入,这时的引入资源与原来的不一样,读取的是事先存在的资源。 不管是原来的还是通过preload预加载的,都是通过触发条件+动态插入script标签引入,如下:
document. querySelector('.btn').addEventListener('click', function () {
var script = document.createElement('script');
script.src = 'demo.js';
document.body.appendChild(script);
});
使用preload 预加载时会在页面开始之初通过 link 插入这样一段代码:
<link rel="preload" href="demo.js" as="script">
注意:用preload 实际上是一种拆东墙补西墙的做法,牺牲一部分首屏渲染时间,换来页面间更为顺畅的导航,这也是单页面导航,服务端SSR渲染常见的方式。
用preload预加载时,在页面开始之初,会多出一个请求,只不过这个请求是在浏览器空闲时完成的,而后续的操作如果需要这条资源时就可以不用请求服务器,这样在整体上页面性能更优,
当你自己实践的时候可以看到,perload加载所花的时间与正常加载所花的时间虽然差不多,但此时点击按钮,浏览器并没有再次请求point.js,而是直接执行已预先加载好的demo.js,这样就少了一次请求。
而传统方式,虽然时间和预加载时间相差无几,但是你每点击一次都会请求一次
总结
. preload
-
用途: 提前加载当前页面 需要的关键资源,以优化加载速度。
-
特点:
- 高优先级加载:告诉浏览器这是一个关键资源,必须尽早加载。
- 适合当前页面渲染需要的 CSS、JS、字体、图片等。
- 一旦加载完成,资源立即可用。
-
常见使用场景:
- 提前加载字体文件:
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin="anonymous"> - 提前加载脚本:
<link rel="preload" href="main.js" as="script">
- 提前加载字体文件:
prefetch
-
用途: 提前加载 未来可能需要的资源,以便后续页面访问时加快加载速度。
-
特点:
- 低优先级加载:浏览器只会在空闲时间加载,不会阻塞当前页面的渲染。
- 适合预加载下一步交互或即将访问的页面资源。
- 不一定会被加载(浏览器可能跳过)。
-
常见使用场景:
-
预加载下一页面的 JS 文件:
<link rel="prefetch" href="next-page.js"> -
预加载大文件(用户可能点击的链接资源):
<link rel="prefetch" href="large-image.jpg">
-
img标签
<img>标签跟性能提升有什么关系呢?🤔
众所周知,<img>标签主要用于加载放置图片资源,而在一些多图网站中,可以通过预置小图,再懒加载大图的方式提高性能。这里我后面也会讲,这里不做阐述,这里要说的是<img>标签的设置对浏览器渲染的影响。
在一些网站中,某段时间在某个位置放了一张图片,这个时间过后图片可能会变动成另外一张。由于浏览器默认的layout方式是文档流,很多时候页面内容的变动都伴随着Repaint,处理不好时还会有Reflow。
假如这个位置的图片通过区域无刷更新的方式换了一张照片,而这张新的照片大小与原照片不一致,并且此时对<img>标签设置得不合理(比如未设定好固定的宽高),浏览器就会根据照片的大小,重新计算页面要展示的位置。就因为这一个小小的变动,基于文档流的后续节点都要跟着调整,最终造成整个页面重排,这对性能的影响无疑是很大的。
这种情况也有解决办法,可以给<img>标签设定一个固定的大小或外套一个其他设定好大小的标签,再将图片宽高设置成100%。
<img>标签中有一个 alt 属性,一般有两个使用场景
- 用于填写对图片的描述,
- 也用于当图片无法正常显示时显示图片的替换文字。
笔者看过不少开发的代码,alt 属性很多时候都是空的,可能是要兼顾开发效率,也能是因为图片src变动较多,或者可能是图片本身并没有太大意义。
其实,少了alt属性值对性能没有什么影响,但在SEO优化中会产生影响!!。目前的爬虫程序对于各种媒体标签,如audio、video等,实际上只会抓取<img>标签,所以<img>标签中的alt属性就比较重要了。alt属性是搜索引擎判断图片与文字是否相关的重要依据,alt属性添加到<img>中的主要目的实际上是优化搜索引擎,比如网站的图片想要在百度图库中被找到,百度图库有跳转到源链接功能的功能,这也是流量的一种入口
总结一下:
- 可以给<img>标签设定一个固定的大小或外套一个其他设定好大小的标签,再将图片宽高设置成100%。避免出现重绘甚至回流
- 使用alt属性用于seo优化
自定义属性
自定义属性是指在 HTML(超文本标记语言)中,开发人员自行定义添加到 HTML 元素上的属性,它们不属于 HTML 标准规范里预先定义好的那些常规属性(如 id、class、src、href 等),而是根据具体业务需求额外创建的属性,通常用于存储和传递特定的数据或信息,以方便在网页开发过程中实现更多功能、处理各种交互逻辑等。
比如
<div id="myDiv" data-my-custom-attribute="这是一个自定义属性的值">
这是一个包含自定义属性的 div 元素。
</div>
这里data-my-custom-attribute就是自定义属性,后面的内容是它的值
笔者在看过别人自定义属性滥用的情况,一个元素上面写了四五个自定义属性,这会造成什么影响呢? 让我们看看下面的例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="myDiv" class="my-class" data-custom-attribute="example-value" data-another-attr="another-value">
这是一个示例 div 元素
</div>
</body>
</html>
我们可以看到每多一个属性就会向DOM 树对应的位置填入该属性并附上对应的值。如果属性很多,这个 attributes 会变得很大。单个元素这样做时可能影响并不大,但假如页面有非常多的元素就会对性能产生一定的影响。不仅在DOM 创建之初需要增加key和存上对应的值,后续数组的遍历同样会变慢。
大量元素都有很多属性时的情况
- DOM 创建阶段的影响:
在浏览器解析 HTML 文档去构建 DOM 树的初始阶段,对于每一个元素都需要解析它的众多属性,然后在 DOM 树对应的节点位置为每个属性创建对应的 “键值对” 存储结构(也就是增加key表示属性名,并存储上对应的属性值)。元素和属性数量众多意味着这个创建过程要做大量这样的操作,会消耗更多的时间和计算资源,从而使得 DOM 树的构建速度变慢,进而拖慢整个页面的首次加载速度。 - 后续遍历操作的影响:
在页面后续的运行过程中,常常需要对 DOM 元素进行遍历操作,比如使用 JavaScript 通过循环来查找满足某些条件的元素、更新元素的样式或者内容等。当每个元素都带有大量属性时,在遍历过程中,浏览器或 JavaScript 引擎就需要逐个去读取每个元素节点上的众多属性,这无疑增加了遍历的工作量,导致遍历操作变得更慢,影响了页面交互的响应速度以及一些动态更新功能的执行效率。
总结:
合理的自定义属性可以使页面传值、参数传递变得更加便捷、快速。
比较推荐的一个做法是,将一个DOM块,比如一个组件单元的顶层,设置为自定义属性,因为一般情形下一个后台的请求会对应界面一块区域的显示。这样一块区域就可以作为一个单元,整个页面自定义的属性总数就不会很多了。
类似这样的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>宠物信息页</title>
</head>
<body>
<div class="pet-section cats">
<h2>猫咪信息</h2>
<div class="pet-item" data-pet-name="咪咪" data-pet-type="猫" data-pet-age="3" data-pet-color="白色">
<p>宠物名字:<span data-display-name="咪咪"></span></p>
<p>宠物种类:<span data-display-type="猫"></span></p>
<p>宠物年龄:<span data-display-age="3"></span></p>
<p>宠物颜色:<span data-display-color="白色"></span></p>
<button onclick="showDetails(this)" data-button-action="show" data-button-pet="咪咪">查看详情</button>
</div>
<div class="pet-item" data-pet-name="花花" data-pet-type="猫" data-pet-age="2" data-pet-color="橘色">
<p>宠物名字:<span data-display-name="花花"></span></p>
<p>宠物种类:<span data-display-type="猫"></span></p>
<p>宠物年龄:<span data-display-age="2"></span></p>
<p>宠物颜色:<span data-display-color="橘色"></span></p>
<button onclick="showDetails(this)" data-button-action="show" data-button-pet="花花">查看详情</button>
</div>
</div>
<div class="pet-section dogs">
<h2>狗狗信息</h2>
<div class="pet-item" data-pet-name="旺财" data-pet-type="狗" data-pet-age="4" data-pet-color="黑色">
<p>宠物名字:<span data-display-name="旺财"></span></p>
<p>宠物种类:<span data-display-type="狗"></span></p>
<p>宠物年龄:<span data-display-age="4"></span></p>
<p>宠物颜色:<span data-display-color="黑色"></span></p>
<button onclick="showDetails(this)" data-button-action="show" data-button-pet="旺财">查看详情</button>
</div>
<div class="pet-item" data-pet-name="大黄" data-pet-type="狗" data-pet-age="5" data-pet-color="黄色">
<p>宠物名字:<span data-display-name="大黄"></span></p>
<p>宠物种类:<span data-display-type="狗"></span></p>
<p>宠物年龄:<span data-display-age="5"></span></p>
<p>宠物颜色:<span data-display-color="黄色"></span></p>
<button onclick="showDetails(this)" data-button-action="show" data-button-pet="大黄">查看详情</button>
</div>
</div>
<script>
function showDetails(button) {
const petName = button.dataset.buttonPet;
const action = button.dataset.buttonAction;
console.log(`执行 ${action} 操作,查看 ${petName} 的详情`);
}
</script>
</body>
</html>
标签的src属性及href属性
标签的 src 属性及 href属性一般用于加载资源。href表示超文本引用,用在link、a 等元素上,它表示引用和页面关联,是在当前元素和引用资源之间建立联系。src表示某个资源的路径,用在<img>、<script>、<iframe> 上,
src是页面内容不可缺少的一部分。 用这两个属性有时候会产生问题,比如src属性和<link>标签的href属性为空(值为空)时,一些浏览器可能把当前页面当成属性值加载。 这种问题很多时候是很难查找出来 的,可能只是觉得页面较慢,
当开发人员在拼命找哪个请求很慢的时候,已经走偏了,因为实际上加载了两个页面,一个页面嵌套在另一个页面中。
在正常的问题排查过程中,很少有人去将整个DOM结构看一遍,费时费力且找到问题的可能性还很低,所以这就要求开发人员在平时的开发过程中养成良好的习惯,不滥用自带href、src属性的标签,有些不需要的标签可以用类似于 a标签的写法
<a href="javascript:;"></a>
来杜绝此类问题,尽量不弄成空的。