什么是DNS呢?简单来说,DNS 的作用是将域名解析为 IP 地址,解析的过程是耗时的,转化后会做本地缓存,我们的优化的目标主要是针对用户第一次访问站点的时候陷入长时间白屏的问题。
DNS 解析可以分为两类,第一类是页面 DNS 解析,当用户输入 url 地址后(比如是第一次访问站点),便开始页面 DNS 解析,这个过程是省不了的,因为你无法在用户访问站点之前就让他提前把 DNS 解析好;
第二类是其他资源的 DNS 解析,在浏览器解析 html 的时候,会遇到一些 script 元素、link 元素,此时会暂停 html 的解析,转而加载 JS,里面就包含了 DNS 解析,这个过程是耗时的,会阻塞浏览器渲染主线程,所以该如何进行优化呢?答案是采用 DNS 预解析!
DNS预解析(dns-prefetch
)是前端网络性能优化的一种措施,它根据浏览器定义的规则,提前解析之后可能会用到的域名,使解析结果缓存到系统缓存中,缩短DNS解析时间,进而提高网站的访问速度。
DNS预解析能够让浏览器在用户访问链接之前解析域名,其范围包括文档的所有链接,包括图片、CSS、JS;域名解析后,如果用户确实访问该域名,那么DNS解析时间将不会有延迟。因为预读取会在后台执行,所以DNS很可能在链接对应的东西出现之前就已经解析完毕,这能够减少用户点击链接时的延迟。
如何开启DNS预解析?
在 HTML 的 head 部分添加以下代码来启用 DNS 预解析,href 属性指定了需要预解析的主机名:
<link rel="dns-prefetch" href="//baidu.com">
需要注意的是,
dns-prefetch
仅对跨域域上的 DNS 查找有效,因此请避免使用它来指向相同域
那如何在vite项目中开启呢?
在package.json中配置:
// package.json
"scripts": {
"build": "vite bulid && node ./scripts/dns-prefetch.js"
}
然后配置解析代码,简单来说就是,遍历打包后的 dist 目录中的所有 HTML、JS、CSS 文件,将所有外链的域名存起来,然后在 dist 目录下 index.html 文件的 head 标签中依次插入 link 标签,同时开启 DNS 预解析,如下:
// dns-prefetch.js
const fs = require('fs')
const path = require('path')
const { parse } = require('node-html-parser')
const { glob } = require('glob')
const urlRegex = require('url-regex')
// 获取外部链接的正则表达式
const urlPattern = /(https?://[^/]*)/i
const urls = new Set()
// 遍历dist目录中的所有HTML、JS、CSS文件
async function searchDomin() {
const files = await glob('dist/**/*.{html,css,js}')
for (const file of files) {
const source = fs.readFileSync(file, 'utf-8')
const matches = source.match(urlRegex({ strict: true }))
if (matches) {
matches.forEach((url) => {
const match = url.match(urlPattern)
if (match && match[1]) {
urls.add(match[1])
}
})
}
}
}
// 在index.html文件<head>标签中插入link标签
async function insertLinks() {
const files = await glob('dist/**/*.html')
const links = [...urls].map((url) => `<link rel="dns-prefetch" href="${url}" />`).join('\n')
for (const file of files) {
const html = fs.readFileSync(file, 'utf-8')
const root = parse(html)
const head = root.querySelector('head')
head.insertAdjacentHTML('afterbegin', links)
fs.writeFileSync(file, root.toString())
}
}
async function main() {
await searchDomin()
await insertLinks()
}
main()