When users try to visit our application, example.com
for the first time, DNS need to parsing and find out application IP address
.
After first time visiting, the IP address and domain name mapping will be cached for subsequence visit.
So optimizing DNS parsing is actually for the first time visit.
When parseing DNS for the application (the first red box), this is something we cannot avoid and optimize;
The one we can optimize is the second & third red box, is when browser trying to load js, css, image from other domains
// load js from cdn.com
// load css from other.com
// load image from imgs.com
When parsing those DNS, it not only take time, but also will block rendering.
The solution is let browser parsing the DNS beforehands
<head>
<link rel="dns-prefetch" href="https://www.coffee.com" />
<link rel="dns-prefetch" href="https://www.autoimg.com" />
<link rel="dns-prefetch" href="https://www.abc.com" />
</head>
<style>
body {
background: url('https://www.coffee.com/bg.com')
}
</style>
<body>
<img src="https://www.autoimg.com/img/234234.jpg" alt="" />
<script src="https://www.abc.com.sss.js"></script>
</body>
So we use dns-prefetch
with the domian
to ask browser to pre-fetch the resouces files.
While this works for simple static files, but in large application with morden framework, we might requires lots of reseouces files from different domains, it's kind of hard to manually tracking those domains.
// scripts/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');
// Test Regex
const urlPattern = /(https?:\/\/[^/]+)/i;
const urls = new Set();
// Search for all HTML, JS, CSS files in the 'dist' directory
async function searchDomain() {
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]);
}
});
}
}
}
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 searchDomain();
// Add prefetch links in the <head> tag
await insertLinks();
}
main();
We want to run this script after bundling finished. Therefore we can create a helper scripts in package.json:
build: vite build && node ./scripts/dns-prefetch.js