前端优化
脚本异步载入
传统的脚本加载方式是阻塞式的,即在脚本加载和执行完成之前,浏览器会阻塞页面的渲染。而异步加载脚本采用非阻塞方式,页面会继续渲染,而不必等待脚本的加载和执行。
异步加载脚本的实现方式主要有两种:
async
使用 async
属性: 将 script
标签的 async
属性设置为 true
,浏览器将异步加载和执行脚本,加载完成后立即执行,不阻塞页面渲染。
<script async src="example.js"></script>
使用动态创建 script
元素: 使用 JavaScript
动态创建 script
元素,并通过设置 async
属性实现异步加载。
const script = document.createElement('script') script.src = 'example.js' script.async = true document.head.appendChild(script)
defer
defer
属性与 async
属性类似,也用于异步加载脚本,但有一个重要的区别:defer
保证脚本的执行顺序与它们在页面中的顺序一致。
<script defer src="script1.js"></script> <script defer src="script2.js"></script> <script defer src="script3.js"></script> // 按script1 script2 script3依次执行
优势
1、提高页面加载速度
异步加载脚本不会阻塞页面渲染,因此可以加速页面加载速度。尤其对于大型应用或包含多个脚本的页面,异步加载可以更高效地利用网络资源。
2、提升用户体验
通过异步加载脚本,可以使页面更快地呈现给用户,提升用户体验。特别是在移动设备或网络状况较差的情况下,异步加载对提高页面的响应速度更为显著。
3、并行加载多个脚本
异步加载脚本使得多个脚本能够并行加载,而不是串行加载。这意味着页面可以同时下载多个脚本文件,加快整体加载时间。
4、动态加载第三方库
在某些情况下,我们可能只有在特定条件下才需要加载某个第三方库。通过动态创建 script
元素,可以根据条件在运行时决定是否加载该库。
if (someCondition) { const script = document.createElement('script') script.src = 'third-party-library.js' script.async = true document.head.appendChild(script) }
5、异步加载并执行
在某些情况下,可能需要等待一个异步操作完成后再加载并执行脚本。可以使用 onload
事件或 Promise
来实现。
const script = document.createElement('script') script.src = 'example.js' script.async = true // 使用 onload 事件 script.onload = () => { console.log('Script loaded and executed.') document.head.appendChild(script) } // 或者使用 Promise function loadScript(src) { return new Promise((resolve, reject) => { const script = document.createElement('script') script.src = src script.async = true script.onload = resolve script.onerror = reject document.head.appendChild(script) }) } loadScript('example.js').then(() => { console.log('Script loaded and executed.') })
注意
1、依赖关系
异步加载脚本可能导致脚本之间的依赖关系变得复杂。在使用异步加载脚本时,需要确保脚本的加载顺序不会破坏它们之间的依赖关系。
2、兼容性
虽然大多数现代浏览器都支持异步加载脚本的方式,但在某些旧版本浏览器中可能存在兼容性问题。在实际应用中,需要根据项目的浏览器兼容性要求做出相应的选择。
预加载和预渲染
预加载
有些资源不需要马上用到,但是希望尽早获取,这时候就可以使用预加载。预加载可以一定程度上降低首屏的加载时间,因为可以将一些不影响首屏但重要的文件延后加载,唯一缺点就是兼容性不好
预加载其实是声明式的 fetch
,强制浏览器请求资源,并且不会阻塞 onload
事件,可以使用以下代码开启预加载:
<link rel="preload" href="http://example.com" />
预渲染
可以通过预渲染将下载的文件预先在后台渲染,预渲染虽然可以提高页面的加载速度,但是要确保该页面百分百会被用户在之后打开,否则就白白浪费资源去渲染,可以使用以下代码开启预渲染:
<link rel="prerender" href="http://example.com" />
HTTP 缓存
浏览器询问服务器缓存是否有效,服务器返回 304 指示浏览器使用缓存。
资源仍然处于有效期时,浏览器会直接使用磁盘缓存。
Cache-Control
响应头表示了资源是否可以被缓存,以及缓存的有效期。
Etag
响应头标识了资源的版本,此后浏览器可据此进行缓存以及询问服务器。
Last-Modified
响应头标识了资源的修改时间,此后浏览器可据此进行缓存以及询问服务器。
Cache-Control
Cache-Control
在 HTTP
响应头中,用于指示代理和 UA(User Agent,简称 UA,浏览器标识,是一种用于标识浏览器类型、版本和操作系统等信息的字符串) 使用何种缓存策略
no-cache
为本次响应不可直接用于后续请求(在没有向服务器进行校验的情况下)
no-store
为禁止缓存(不得存储到非易失性介质,如果有的话尽量移除,用于敏感信息)
private
为仅 UA 可缓存
public
为大家都可以缓存。
当 Cache-Control
为可缓存时,同时可指定缓存时间(比如 public
, max-age:86400
)。 这意味着在 1 天(60x60x24=86400
)时间内,浏览器都可以直接使用该缓存(此时服务器收不到任何请求)
import http from 'http' let server = http.createServer((req, res) => { res.setHeader('Cache-Control', 'public, max-age=86400') res.end('harttle.com') }) server.listen(3333)
除了 Cache-Control
中的 max-age
外,Expires
,Vary
等头字段也可用来设置缓存的有效性。
Etag
如果资源本身确实会随时发生改动,还用 Cache-Control
就会使用户看到的页面得不到更新。 但如果还希望利用 HTTP 缓存(万一资源没变呢),这就需要有条件的(conditional)HTTP 请求。
Etag
响应头字段表示资源的版本,浏览器在发送请求时会带 If-None-Match
头字段, 来询问服务器该版本是否仍然可用。如果服务器发现该版本仍然是最新的, 就可以返回 304 状态码指示 UA 继续使用缓存。
import http from 'http' let server = http.createServer((req, res) => { console.log(req.url, req.headers['if-none-match']) if (req.headers['if-none-match']) { // 检查文件版本 res.statusCode = 304 res.end() } else { res.setHeader('Etag', '00000000') res.end('harttle.com') } }) server.listen(3333)
Last-Modified
与 Etag
类似,Last-Modified
HTTP 响应头也用来标识资源的有效性。 不同的是使用修改时间而不是实体标签。对应的请求头字段为 If-Modified-Since
import http from 'http' let server = http.createServer((req, res) => { console.log(req.url, req.headers['if-modified-since']) if (req.headers['if-modified-since']) { // 检查时间戳 res.statusCode = 304 res.end() } else { res.setHeader('Last-Modified', new Date().toString()) res.end('harttle.com') } }) server.listen(3333)
页面刷新
按下刷新按钮或快捷键(在 MacOS 中是 Cmd+R)会触发浏览器的“正常重新加载”(normal reload), 此时浏览器会执行一次 Conditional GET
。 Cache-Control
等缓存头字段会被忽略,并且带 If-None-Match
, If-Modified-Since
等头字段。 此时服务器总会收到一次 HTTP GET 请求。 在 Chrome 中按下刷新,浏览器还会带如下请求头:Cache-Control:max-age=0
在 Chrome 中按下 Cmd+Shift+R (MacOS)可以触发强制重新加载(Hard Reload), 此时包括页面本身在内的所有资源都不会使用缓存。 浏览器直接发送 HTTP 请求且不带任何条件请求字段。
nginx 代码压缩
Nginx
开启 Gzip
压缩功能, 可以使网站的 css
、js
、xml
、html
文件在传输时进行压缩,提高访问速度, 开启 Gzip
功能后,Nginx
服务器会根据配置的策略对发送的内容, 如 css
、js
、xml
、html
等静态资源进行压缩, 使得这些内容大小减少,在用户接收到返回内容之前对其进行处理,以压缩后的数据展现给客户。
这样不仅可以节约大量的出口带宽,提高传输效率,还能提升用户快的感知体验, 一举两得; 尽管会消耗一定的 cpu 资源,但是为了给用户更好的体验还是值得的
在 nginx 中配置文件 http{}
中这样写
http { ....... gzip on; // 开启 gzip 压缩功能 gzip_min_length 1k; // 设置允许压缩的页面最小字节数; 这里表示如果文件小于 10 个字节,就不用压缩,因为没有意义,本来就很小 gzip_buffers 4 16k; // 设置压缩缓冲区大小,此处设置为 4 个 16K 内存作为压缩结果流缓存 gzip_http_version 1.1; // 压缩版本 gzip_comp_level 6; //设置压缩比率,最小为 1,处理速度快,传输速度慢;9 为最大压缩比,处理速度慢,传输速度快; 这里表示压缩级别,可以是 0 到 9 中的任一个,级别越高,压缩就越小,节省了带宽资源,但同时也消耗 CPU 资源,所以一般折中为 6 gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json; // 制定压缩的类型,线上配置时尽可能配置多的压缩类型! gzip_disable "MSIE [1-6]\."; // 配置禁用 gzip 条件,支持正则。此处表示 ie6 及以下不启用 gzip(因为 ie 低版本不支持) gzip_vary on; // 选择支持 vary header;改选项可以让前端的缓存服务器缓存经过 gzip 压缩的页面; 这个可以不写,表示在传送数据时,给客户端说明我使用了 gzip 压缩 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2022-05-07 Dos命令
2022-05-07 CSS技巧