包阅导读总结
1. 前端、网页开发、部署发布、缓存控制、关键路径渲染
2. 本文围绕前端代码如何被用户看到展开,介绍了网页构成、浏览器工作原理、现代化开发过程、部署发布流程及缓存控制策略,强调了非覆盖式发布和精确缓存控制的重要性。
3.
– 网页入口
– 用户输入有效地址,浏览器向服务器请求网页入口文件“xxx.html”。
– 浏览器解析 HTML 代码,识别其他资源并发起请求,逐步呈现完整页面,涉及关键路径渲染(CRP)。
– 开发
– 采用组件式开发,使用 UI 框架、CSS 预处理器和前端构建工具。
– 构建工具提供模块化等能力,针对生产环境进行优化处理。
– 部署与发布
– 开发阶段可本地运行开发服务器或手动上传至服务器访问。
– 发布平台可自动化流程,包括卡口检查、云构建等。
– 考虑安全发布与用户体验,解决页面更新的缓存问题,采用非覆盖式发布。
– 静态资源存于 CDN,动态页面存于离业务服务器近的地方,先全量发布静态资源再灰度推全量发布页面。
思维导图:
文章地址:https://mp.weixin.qq.com/s/p4uvQA1_IPU4uT6C0zN7UA
文章来源:mp.weixin.qq.com
作者:飞流
发布时间:2024/8/29 9:17
语言:中文
总字数:1581字
预计阅读时间:7分钟
评分:84分
标签:前端开发,网页部署,性能优化,HTML,CSS
以下为原文内容
本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com
这是2024年的第63篇文章
( 本文阅读时间:15分钟 )
-
HTML 决定网页内容,是用户访问任意一个网站的入口,既可以在 HTML 中直接编写 CSS、JS 代码,也可以将 CSS、JS 代码写在单独的文件中在 HTML 引入;
<!DOCTYPE html>
<html>
<head>
<title>网页标题,在浏览器打开的网页tab上显示</title>
<meta name="keywords" content="网页关键词,SEO"/>
<meta name="description" content="网页描述,SEO"/>
<style>
.foo {
color: red;
}
</style>
<link rel="stylesheet" href="https://x.alicdn.com/xx/xxx/screen.css"/>
</head>
<body>
<div class="foo">
Page Content
</div>
<script>
function log(param) {
console.log(param)
}
log('解析并执行这段js代码')
</script>
<script src="https://x.alicdn.com/xx/xxx/screen.js"></script>
</body>
</html>
用户访问任意网站之前,要先在地址栏输入一个有效地址,接着浏览器会向服务器发起请求,去拿到该地址对应的网页入口文件即”xxx.html”,打开浏览器 Network 控制台便可以看到,这一定是浏览器第一个接收到的响应内容。 紧接着,浏览器解析 HTML 代码,识别到其他资源发起更多请求,经过各种类型资源的加载、解析、执行(非必需)逐步成为用户眼前看到的完整页面,讲到这里就不得不提到 CRP(Critical Rendering Path,关键路径渲染),即浏览器将 HTML、JS、CSS 代码转换成屏幕上用户可见像素必经的一系列关键步骤,如下:
-
网络下载 HTML,解析 HTML 代码构建 DOM; -
网络下载 CSS,解析 CSS 代码构建 CSSOM; -
网络下载 JS,解析执行 JS 代码,可能会修改 DOM 或 CSSOM; -
待 DOM & CSSOM “定型”,浏览器根据 DOM 和 CSSOM 构造 Render Tree;
import '@/common/style.scss'
import arrowBack from '@/common/arrow-back.svg'
import { loadScript } from '@/common/utils.js'
区别于开发阶段,构建工具还针对生产环境提供了丰富的构建能力,能将业务源码进行压缩、tree-shaking 优化,uglify 混淆、兼容、extract 抽离等处理,成为适用于生产环境的最优代码。 !function(){"use strict";function t(t){if(null==t)return-1;var e=Number(t);return isNaN(e)?-1:Math.trunc(e)}function e(t){var e=t.name;return/(\.css|\.js|\.woff2)/.test(e)&&!/(\.json)/.test(e)}function n(t){var e="__";return"".concat(t.protocol).concat(e).concat(t.name).concat(e).concat(t.decodedBodySize).concat(e).concat(t.encodedBodySize).concat(e).concat(t.transferSize).concat(e).concat(t.startTime).concat(e).concat(t.duration).concat(e).concat(t.requestStart).concat(e).concat(t.responseEnd).concat(e).concat(t.responseStart).concat(e).concat(t.secureConnectionStart)}var r=function(){return/WindVane/i.test(navigator.userAgent)};function o(){return r()}function c(){return!!window.goldlog}var i=function(){return a()},a=function(){var t=function(t){var e=document.querySelector('meta[name="'.concat(t,'"]'));if(!e)return;return e.getAttribute("content")}("data-spm"),e=document.body&&document.body.getAttribute("data-spm");return t&&e&&"".concat(t,".")......
@charset "UTF-8";.free-shipping-block{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center;background-color:#ffe8da;background-position:100% 100%;background-repeat:no-repeat;background-size:200px 100px;border-radius:8px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;margin-top:24px;padding:12px}.free-shipping-block .content{-webkit-box-flex:1;-ms-flex-positive:1;color:#4b1d1f;-webkit-flex-grow:1;flex-grow:1;font-size:14px;margin-left:8px;margin-top:0!important}.free-shipping-block .content .desc img{padding-top:2px;vertical-align:text-top;width:120px}.free-shipping-block .co.....
<!doctype html><html><head><script defer="defer" src="/build/xxx.js"></script>
<link href="/build/xxx.css" rel="stylesheet"></head><body><div id="root"></div>
</body></html>
-
检查分支提交信息、必须配置、依赖合规检查等系列卡口; -
运行脚本,执行事先配置好的依赖安装及打包构建指令,开启云构建,安装项目依赖,并打包一份生产环境产物(说白了云构建这一步就跟我们刚刚 git clone 项目到本地初始化运行、本地 build 是一样的);
.foo {
background-color: red;
}
对于 index.css,如果用户每次打开页面都要重新发起对该文件的请求,不仅浪费带宽而且用户还要多等待一段下载时间,完全可以利用 HTTP 缓存中的强缓存将静态资源缓存在浏览器本地,使用户更快看到页面(快体现在浏览器直接从 memory/dist cache 中读取文件,省去了下载时间)。 Cache-Control: max-age=2592000,s-maxage=86400
对于静态资源,服务器往往设置一个非常大的缓存过期时间以充分利用缓存,这样浏览器就彻底不用发起请求了。但是浏览器都不发请求了,如果我们页面有更新/bug 修复该怎么办呢?很容易想到的办法是在资源 url 上拼接版本号,如: <!doctype html>
<html>
<head>
<script defer="defer" src="https://x.alicdn.com/build/foo.js?t=0.0.1"></script>
<link href="https://x.alicdn.com/build/index.css?t=0.0.1" rel="stylesheet">
</head>
<body>
<div class="foo"></div>
</body>
</html>
下次更新时更换版本号就能强制让浏览器重新发起新的请求: <!doctype html>
<html>
<head>
<script defer="defer" src="https://x.alicdn.com/build/foo.js?t=0.0.2"></script>
<link href="https://x.alicdn.com/build/index.css?t=0.0.2" rel="stylesheet">
</head>
<body>
<div class="foo"></div>
</body>
</html>
但这样做存在一个问题,HTML 同时引用了多个文件,如果在一次迭代中只变更了其中的某个文件,其他文件没做修改,统一加版本号的方法岂不是连带让其他文件的本地缓存都失效了! 为解决这个问题,就得实现文件级别粒度的缓存控制,我们很容易想到 HTTPS 中的数据摘要算法,根据文件内容生成唯一 hash 值,文件无修改 hash 值不变,这样就能精确到单个文件的缓存了: <!doctype html>
<html>
<head>
<script defer="defer" src="https://x.alicdn.com/build/foo.js"></script>
<link href="https://x.alicdn.com/build/index_1i0gdg6ic.css" rel="stylesheet">
</head>
<body>
<div class="foo"></div>
</body>
</html>
或者通过迭代版本号加入资源路径 Path 的方式: <!doctype html>
<html>
<head>
<script defer="defer" src="https://x.alicdn.com/0.0.2/build/foo.js"></script>
<link href="https://x.alicdn.com/0.0.2/build/index.css" rel="stylesheet">
</head>
<body>
<div class="foo"></div>
</body>
</html>
现代前端部署方案,往往将静态资源(JS、CSS、图片等)往往上传到离用户更近的 CDN 上,这些资源基本不怎么改变,需要充分利用缓存提高缓存命中率;而动态页面(HTML)用户数据千人千面、为 SEO 做 SSR,以及为了性能同构,往往存放在离业务服务器更近的地方,取数查数注入数据更快。 两种资源分布在不同地方,那么静态资源就以 CDN 链接引入的方式写于 HTML 中,那么问题来了,我们在更新页面时先发布静态资源还是先发布页面呢? <!doctype html>
<html>
<head>
<script defer="defer" src="https://x.alicdn.com/0.0.1/build/foo.js"></script>
<link href="https://x.alicdn.com/0.0.1/build/index.css" rel="stylesheet">
</head>
<body>
<div class="bar"></div>
</body>
</html>
静态资源发布完成前,期间用户访问到新的页面结构,但是静态资源还是老的,用户可能会看到一个样式错乱的页面,也可能因旧的 JS 脚本找不到元素节点而执行错误的白屏页面,不可行🙅。 <!doctype html>
<html>
<head>
<script defer="defer" src="https://x.alicdn.com/0.0.2/build/foo.js"></script>
<link href="https://x.alicdn.com/0.0.2/build/index.css" rel="stylesheet">
</head>
<body>
<div class="foo"></div>
</body>
</html>
页面发布完成前,页面结构没变,而资源是新的了,如果用户此前访问过,本地存在老资源的缓存,那么他看到的页面是正常的,否则访问到旧页面却加载新资源,还会出现上述一样的问题,要么样式错乱、要么 JS 执行错误导致白屏,不可行🙅。 所以先部署谁都不行!这也是为啥古早上线项目时要辛苦程序员大佬们半夜偷偷上,挑流量低谷时上的缘故了,毕竟影响面能小些。但是哇,这对于大厂来说可没有绝对的低峰期只有相对低峰期。但哪怕是相对低峰期,对于做事追求极致的我们,也是不可接受的! 上面的问题其实是覆盖式发布导致的,当待发布资源覆盖已发布资源时就会出现问题,对应的解决办法就是非覆盖式发布,通过文件路径添加版本号或文件名加 hash,发布新的资源时不覆盖旧的资源,先全量发布静态资源再逐步灰度推全量发布页面,整个问题就完美解决了。
-
采用内容摘要或带版本号的文件路径作为缓存更新依据,做到精确缓存控制; -
静态资源 CDN 部署,节省网络请求传输路径,缩短请求响应时间;
[02]Understanding the critical path
https://web.dev/learn/performance/understanding-the-critical-path
[03]大公司里怎样开发和部署前端代码?
https://www.zhihu.com/question/20790576