移除不必要的空白字符、注释和冗余标签,以减少文件大小
使用命令 npm install html-minifier -g
安装 HTML Minifier
使用命令 html-minifier -V
确认安装成功
在 Node.js 环境中配置 index.js
// 引入 HTML Minifier
const minify = require("html-minifier").minify;
// 处理 HTML 文本
let result = minify('<p title="blah">foo</p>', {
removeAttributeQuotes: true,
});
// 输出处理结果
console.log(result);
使用命令 node .\index.js
运行,输出结果为:<p title=blah id=moo>foo</p>
详细参考:https://www.npmjs.com/package/html-minifier
在线使用:https://kangax.github.io/html-minifier/
精简样式表,避免使用冗余或过时的属性,合理组织选择器以减少计算复杂度
使用命令 npm install cssnano postcss postcss-cli --save-dev
安装 PostCSS 与 CSSNaNo
在 Node.js 环境中配置 postcss.config.js
module.exports = {
plugins: [
require("cssnano")({
preset: "default",
}),
],
};
使用命令 npx postcss input.css > output.css
运行,生成优化后的结果 output.css
其他工具:
使用工具(如 Webpack 等)或在线服务对代码进行压缩,移除空格、注释和不必要的字符
使用命令 npm install terser-webpack-plugin --save-dev
安装 terser-webpack-plugin
在 Node.js 环境中配置 webpack.config.js
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
};
使用命令 npx webpack
运行 Webpack 打包工具,并优化 JavaScript 代码
将多个 CSS、JavaScript 文件合并为一个,减少 HTTP 请求的数量
entry
和 output
选项来自动合并多个模块到一个文件中gulp-concat
插件来合并文件<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
img {
width: 1000px;
height: 700px;
background-color: wheat;
object-fit: cover;
object-position: center;
}
</style>
</head>
<body>
<img src="" data-src="./images/1.jpg" alt="" />
<img src="" data-src="./images/2.jpg" alt="" />
<img src="" data-src="./images/3.jpg" alt="" />
<img src="" data-src="./images/4.jpg" alt="" />
<img src="" data-src="./images/5.jpg" alt="" />
<script>
/**
* 初始化图片懒加载功能。
* 该函数通过监听窗口的滚动事件,来实现图片的延迟加载。当图片进入视口时,将其src属性设置为真正的图片源URL,从而实现懒加载的效果。
*/
const imageLazyLoad = () => {
// 获取页面中所有带有data-src属性的图片元素
const imgs = document.querySelectorAll("img");
// 定义计算函数,用于检查图片是否进入视口
const calc = () => {
imgs.forEach((img) => {
// 检查图片是否进入视口:如果图片的顶部位置小于等于窗口的底部位置,则图片已进入视口,可以加载
if (img.offsetTop <= window.innerHeight + window.scrollY)
// 设置图片的src属性为data-src属性的值,真正开始加载图片
img.src = img.dataset.src;
else
// 如果图片未进入视口,则返回,不进行加载
return;
});
};
// 监听窗口的滚动事件,以便在滚动时触发图片的加载
window.addEventListener("scroll", () => calc());
// 初次加载页面时,立即计算并加载可视区域内的图片
calc();
};
// 调用函数,初始化图片懒加载
imageLazyLoad();
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
img {
width: 1000px;
height: 700px;
background-color: wheat;
object-fit: cover;
object-position: center;
}
</style>
</head>
<body>
<img src="" data-src="./images/1.jpg" alt="" />
<img src="" data-src="./images/2.jpg" alt="" />
<img src="" data-src="./images/3.jpg" alt="" />
<img src="" data-src="./images/4.jpg" alt="" />
<img src="" data-src="./images/5.jpg" alt="" />
<script>
/**
* 初始化图片懒加载
* 该函数通过IntersectionObserver API来实现图片的懒加载。只有当图片进入视口时,才会真正加载图片资源
*/
const imageLazyLoad = () => {
// 获取所有需要懒加载的图片元素
const imgs = document.querySelectorAll("img");
// 创建IntersectionObserver实例,用于观察图片是否进入视口
const observer = new IntersectionObserver((entries) => {
// 遍历所有观察到的条目
entries.forEach((entry) => {
// 如果图片进入视口
if (entry.isIntersecting) {
let img = entry.target;
// 设置图片的src属性为data-src属性的值,即真正的图片源地址
img.src = img.dataset.src;
// 停止观察该图片,因为它已经加载
observer.unobserve(img);
}
});
});
// 对所有需要懒加载的图片元素启用IntersectionObserver观察
imgs.forEach((img) => {
observer.observe(img);
});
};
imageLazyLoad();
</script>
</body>
</html>
避免不必要的 DOM 元素,以减少渲染和重绘的时间
使用 CSS 替代 DOM 元素
举例:
<ul>
<li><img src="icon1.png" alt="Icon 1"><span>Item 1</span></li>
<li><img src="icon2.png" alt="Icon 2"><span>Item 2</span></li>
</ul>
优化为
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<style>
.item::before {
content: "";
display: inline-block;
width: 20px;
height: 20px;
background-image: url(icon-based-on-class.png);
background-size: cover;
margin-right: 5px;
}
</style>
引入 Flex 布局与 Grid 布局
举例:
<div>
<div>
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
优化为
<div style="display: flex;">
<div>Item 1</div>
<div>Item 2</div>
</div>
多个动态内容采用 DocumentFragment 添加
举例:
for (let i = 0; i < 100; i++) {
let li = document.createElement("li");
li.textContent = `Item ${i}`;
document.querySelector("ul").appendChild(li);
}
优化为
let fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
let li = document.createElement("li");
li.textContent = `Item ${i}`;
fragment.appendChild(li);
}
document.querySelector("ul").appendChild(fragment);
DocumentFragment 是一个轻量级的文档对象,可以包含节点和子节点,但不会成为文档树的一部分
通过事件委托来减少与 DOM 的交互次数,提高性能
事件委托:一种事件处理的技术,它利用事件冒泡的原理,只在父元素上设置一个事件监听器,而不是在每个子元素上分别设置。当子元素上发生事件时,该事件会冒泡到父元素,父元素上的事件监听器会检查事件源(即触发事件的子元素),并据此执行相应的操作
举例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
</head>
<body>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<button>Add item</button>
<script>
const ul = document.querySelector("ul");
ul.addEventListener("click", (e) => {
if (e.target.tagName === "LI") {
console.log("e.target.textContent", e.target.textContent);
e.target.remove();
}
});
const button = document.querySelector("button");
button.addEventListener("click", () => {
const newItem = document.createElement("li");
newItem.textContent = `Item ${ul.children.length + 1}`;
ul.appendChild(newItem);
});
</script>
</body>
</html>
使用批量更新、虚拟 DOM 等技术减少重绘和回流
将CSS放在 <head>
标签内,以确保页面在加载时能够优先渲染样式
<head>
<link rel="stylesheet" href="style.css" />
</head>
将非首屏必需的 JS 脚本放在文档末尾或使用 async
、defer
属性,避免阻塞渲染
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 同步加载首屏必需脚本 -->
<script src="critical.js"></script>
</head>
<body>
<!-- 页面内容 -->
<!-- 异步加载非首屏且无依赖的脚本 -->
<script async src="non-critical-async.js"></script>
<!-- 异步加载但按顺序执行的非首屏脚本 -->
<script defer src="non-critical-defer.js"></script>
<!-- 将非首屏脚本放在底部(适用于不支持 async/defer 的老旧浏览器) -->
<!-- <script src="non-critical-old-browser.js"></script> -->
</body>
</html>
async
与 defer
async
:完全异步,脚本的加载和执行不会阻塞文档的解析,但多个异步脚本之间执行顺序不一定,不建议用于具有依赖关系的脚本defer
:等到整个文档都被解析和显示之后,再按照脚本在文档中出现的顺序来执行预加载:一种资源提示,它告诉浏览器这个资源对于当前导航立即需要,并且应该被优先下载和解析
使用 <link rel="preload">
预加载关键资源
<head>
<!-- 预加载字体 -->
<link rel="preload" href="fonts/myfont.woff2" as="font" type="font/woff2" crossorigin="anonymous">
<!-- 预加载CSS -->
<link rel="preload" href="styles/main.css" as="style">
<!-- 其他头部标签... -->
</head>
预读取:一种资源提示,它告诉浏览器这个资源可能会在将来的导航中被用到,但不像预加载那样具有紧迫性
使用 <link rel="prefetch">
预读取可能需要的未来资源
<link rel="prefetch" href="details/product-images.jpg">
<link rel="prefetch" href="details/product-details.js">
- Session:以键值对的方式将缓存数据保存在服务器中,并把键值(Session ID)作为 Cookie 返给浏览器
- Token:应对移动互联网不提供 Cookie 的解决方案,将数据哈希加密并保存在移动端的存储系统中
- JWT 是一种广泛应用的 Token 标准
一种用于存储和检索网络请求的响应的接口
可以与 Service Workers 结合使用,实现离线应用和性能优化
Service Workers:在 Web 浏览器中运行的脚本,具备在后台独立于网页运行的能力
- 提供很多高级功能,如离线内容缓存、推送通知、背景数据同步等
场景:精确控制缓存策略和资源缓存,如构建 PWA(Progressive Web Apps)时
轻量级
按需加载
采用代码分割和懒加载技术,将应用拆分成多个小块,并在需要时才加载它们
以 Vue Router 为例,Vue Router 支持动态导入
const routes = [
{
path: "/products",
name: "Products",
// 使用动态导入来懒加载组件
component: () =>
import(/* webpackChunkName: "products" */ "./views/Products.vue"),
},
// 其他路由...
];
采用集成 RUM(Real User Monitoring)工具收集实际用户的加载性能数据
使用合理 HTML 标记以及其特有的属性去格式化文档内容,提高内容可理解性
详细方法参考:HTML语义化 | CSDN-北航程序员小陈
举例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新闻文章标题</title>
</head>
<body>
<header>
<h1>新闻文章标题</h1>
<p>由 <a href="/author-profile">作者姓名</a> 发表于 <time datetime="2023-04-01">2023年4月1日</time></p>
</header>
<main>
<article>
<h2>引言</h2>
<p>这里是引言部分的内容,简要介绍文章的主题和背景。</p>
<h2>正文标题</h2>
<p>这里是正文的第一段,详细阐述文章的主要观点或故事。</p>
<h3>小节标题</h3>
<p>这里是文章中的一个小节,进一步细化或支持主要观点。</p>
<!-- 可以继续添加更多的h2、h3、p等元素来构建文章内容 -->
<footer>
<p>文章结束。</p>
</footer>
</article>
</main>
<aside>
<h2>相关文章</h2>
<ul>
<li><a href="/article1">相关文章1</a></li>
<li><a href="/article2">相关文章2</a></li>
<!-- 更多相关文章链接 -->
</ul>
</aside>
<footer>
<p>版权所有 © 2023 网站名称</p>
</footer>
</body>
</html>
设置 <title>
、<meta>
、<link rel="canonical">
等 SEO 相关标签
举例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<!-- 页面编码,用于处理不同语言的字符串 -->
<meta charset="UTF-8" />
<!-- 设置视口,确保网页在不同设备上正确显示 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- 页面标题,显示在浏览器标签和搜索结果中 -->
<title>页面标题 - 网站名称</title>
<!-- 页面描述,显示在搜索结果中,用于概括页面内容 -->
<meta name="description" content="这里是页面的简短描述,应包含关键词并吸引用户点击。" />
<!-- 页面关键词,虽然现代搜索引擎对keywords标签的重视程度降低,但仍可作为参考 -->
<meta name="keywords" content="关键词1, 关键词2, 关键词3" />
<!-- 指定页面的规范URL,有助于防止内容重复被搜索引擎索引 -->
<link rel="canonical" href="https://www.example.com/your-page-url" />
<!-- 其他可能的元数据 -->
<meta name="author" content="作者姓名或组织" /> <!-- 添加作者信息 -->
<meta name="robots" content="index, follow" /> <!-- 指示搜索引擎索引并跟踪页面上的链接 -->
<!-- 对于响应式网站,可以使用meta标签来适应不同设备 -->
<meta name="HandheldFriendly" content="true" /> <!-- 告诉移动设备,页面适合于手机浏览 -->
<meta name="MobileOptimized" content="320" /> <!-- 指定移动设备的屏幕宽度,以适应响应式设计 -->
<!-- 引入CSS样式 -->
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- 页面内容 -->
<!-- 引入JavaScript脚本 -->
<script src="script.js"></script>
</body>
</html>
结构化数据:一种使用特定格式(如 JSON-LD、Microdata 或 RDFa)来标记网页内容的方式,以便搜索引擎和其他机器能够更容易地理解和处理这些信息
举例:以下是采用 Schema.org 和JSON-LD 格式的结构化数据
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>电影《星际穿越》</title>
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Movie",
"name": "星际穿越",
"image": "https://example.com/movie-poster.jpg",
"director": {
"@type": "Person",
"name": "克里斯托弗·诺兰"
},
"genre": ["科幻", "剧情", "冒险"],
"actor": [
{
"@type": "Person",
"name": "马修·麦康纳希"
},
{
"@type": "Person",
"name": "安妮·海瑟薇"
}
],
"datePublished": "2014-11-07",
"description": "一队探险家利用他们针对虫洞的新发现,超越人类对于太空旅行的极限,从而开始在广袤的宇宙中进行星际航行的故事。",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "8.7",
"reviewCount": "123456"
}
}
</script>
</head>
<body>
<!-- 网页内容 -->
<h1>电影《星际穿越》</h1>
<p>导演:克里斯托弗·诺兰</p>
<p>主演:马修·麦康纳希, 安妮·海瑟薇</p>
<p>类型:科幻, 剧情, 冒险</p>
<p>上映日期: 2014年11月7日</p>
<p>剧情简介:...(详细描述)</p>
</body>
</html>
<img alt="这是一张图片" />
-End-