淺析前端JS腳本放在head與body對加載的影響以及優(yōu)化策略
JS放在不同位置確實(shí)有重要區(qū)別!這涉及到頁面加載性能、用戶體驗(yàn)和代碼執(zhí)行時機(jī)等多個方面。
1. 基礎(chǔ)區(qū)別
放在<head>中
<!DOCTYPE html>
<html>
<head>
<title>頁面標(biāo)題</title>
<!-- JS在head中 -->
<script src="script.js"></script>
<script>
console.log('head中的腳本執(zhí)行');
// 此時DOM還沒有構(gòu)建完成
document.querySelector('#myButton'); // 可能返回null
</script>
</head>
<body>
<div id="content">頁面內(nèi)容</div>
<button id="myButton">點(diǎn)擊按鈕</button>
</body>
</html>
特點(diǎn):
- 腳本會阻塞HTML解析
- DOM元素還未創(chuàng)建,無法直接操作
- 會延遲頁面渲染
放在<body>底部
<!DOCTYPE html>
<html>
<head>
<title>頁面標(biāo)題</title>
</head>
<body>
<div id="content">頁面內(nèi)容</div>
<button id="myButton">點(diǎn)擊按鈕</button>
<!-- JS在body底部 -->
<script src="script.js"></script>
<script>
console.log('body底部的腳本執(zhí)行');
// 此時DOM已經(jīng)構(gòu)建完成
document.querySelector('#myButton'); // 可以正常獲取元素
</script>
</body>
</html>
特點(diǎn):
- DOM元素已經(jīng)創(chuàng)建完成,可以直接操作
- 不會阻塞頁面首次渲染
- 用戶能更快看到頁面內(nèi)容
2. 頁面加載過程詳解
瀏覽器解析流程
// 模擬瀏覽器解析過程
console.log('1. 開始解析HTML');
// 遇到head中的script
console.log('2. 暫停HTML解析');
console.log('3. 下載并執(zhí)行JS文件');
console.log('4. JS執(zhí)行完成,繼續(xù)解析HTML');
// 解析body內(nèi)容
console.log('5. 構(gòu)建DOM元素');
console.log('6. 遇到body底部的script');
console.log('7. 執(zhí)行body底部的JS');
console.log('8. 頁面解析完成');
實(shí)際測試示例
<!DOCTYPE html>
<html>
<head>
<script>
console.time('head-script');
console.log('Head腳本開始執(zhí)行');
// 嘗試獲取DOM元素
const button1 = document.getElementById('test-button');
console.log('Head中獲取按鈕:', button1); // null
// 模擬一些計(jì)算任務(wù)
let sum = 0;
for(let i = 0; i < 1000000; i++) {
sum += i;
}
console.log('Head腳本執(zhí)行完成');
console.timeEnd('head-script');
</script>
</head>
<body>
<h1>頁面標(biāo)題</h1>
<p>這是頁面內(nèi)容</p>
<button id="test-button">測試按鈕</button>
<script>
console.time('body-script');
console.log('Body腳本開始執(zhí)行');
// 嘗試獲取DOM元素
const button2 = document.getElementById('test-button');
console.log('Body中獲取按鈕:', button2); // HTMLButtonElement
console.log('Body腳本執(zhí)行完成');
console.timeEnd('body-script');
</script>
</body>
</html>
3. 性能影響對比
Head中的JS - 阻塞渲染
// head中的大型腳本會阻塞頁面渲染
// script.js (放在head中)
console.log('開始執(zhí)行大型腳本...');
// 模擬復(fù)雜計(jì)算
function heavyComputation() {
let result = 0;
for(let i = 0; i < 10000000; i++) {
result += Math.random();
}
return result;
}
const result = heavyComputation();
console.log('計(jì)算完成:', result);
// 在這個腳本執(zhí)行期間,頁面完全是白屏狀態(tài)
// 用戶看不到任何內(nèi)容
Body底部的JS - 非阻塞渲染
// body底部的相同腳本
// 用戶已經(jīng)能看到頁面內(nèi)容,然后才執(zhí)行這個腳本
console.log('頁面內(nèi)容已經(jīng)可見,現(xiàn)在執(zhí)行腳本...');
function heavyComputation() {
let result = 0;
for(let i = 0; i < 10000000; i++) {
result += Math.random();
}
return result;
}
const result = heavyComputation();
console.log('計(jì)算完成:', result);
4. 現(xiàn)代解決方案 - 腳本屬性
async 屬性
<head>
<!-- async: 異步下載,下載完立即執(zhí)行 -->
<script src="analytics.js" async></script>
<script src="ads.js" async></script>
</head>
<body>
<div>頁面內(nèi)容</div>
</body>
// async腳本的執(zhí)行時機(jī)不確定
// analytics.js
console.log('Analytics腳本執(zhí)行'); // 可能在DOM準(zhǔn)備好之前或之后執(zhí)行
// ads.js
console.log('廣告腳本執(zhí)行'); // 執(zhí)行順序無法保證
defer 屬性
<head>
<!-- defer: 延遲執(zhí)行,等DOM構(gòu)建完成后按順序執(zhí)行 -->
<script src="jquery.js" defer></script>
<script src="main.js" defer></script>
</head>
<body>
<div>頁面內(nèi)容</div>
</body>
// defer腳本會按順序執(zhí)行,且在DOM準(zhǔn)備好之后
// jquery.js
window.$ = function() { /* jQuery實(shí)現(xiàn) */ };
console.log('jQuery加載完成');
// main.js (會在jquery.js之后執(zhí)行)
$(document).ready(function() {
console.log('DOM準(zhǔn)備完成,jQuery可用');
});
5. 實(shí)際應(yīng)用場景
必須放在Head中的情況
<head>
<!-- 1. 頁面配置腳本 -->
<script>
// 全局配置,需要在頁面渲染前設(shè)置
window.APP_CONFIG = {
apiUrl: 'https://api.example.com',
theme: 'dark'
};
</script>
<!-- 2. 字體加載優(yōu)化 -->
<script>
// 字體預(yù)加載,避免FOUT (Flash of Unstyled Text)
if ('fonts' in document) {
document.fonts.load('1em Arial');
}
</script>
<!-- 3. 關(guān)鍵CSS內(nèi)聯(lián) -->
<script>
// 根據(jù)條件動態(tài)插入關(guān)鍵CSS
const criticalCSS = `
body { font-family: Arial; }
.hero { background: #333; }
`;
const style = document.createElement('style');
style.textContent = criticalCSS;
document.head.appendChild(style);
</script>
<!-- 4. 用戶代理檢測 -->
<script>
// 需要在頁面渲染前確定設(shè)備類型
window.isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if (window.isMobile) {
document.documentElement.classList.add('mobile');
}
</script>
</head>
應(yīng)該放在Body底部的情況
<body>
<header>
<h1>網(wǎng)站標(biāo)題</h1>
<nav id="navigation"><!-- 導(dǎo)航內(nèi)容 --></nav>
</header>
<main>
<article id="content"><!-- 主要內(nèi)容 --></article>
<aside id="sidebar"><!-- 側(cè)邊欄 --></aside>
</main>
<footer>
<p>版權(quán)信息</p>
</footer>
<!-- 這些腳本放在底部 -->
<script>
// 1. DOM操作腳本
const navigation = document.getElementById('navigation');
navigation.addEventListener('click', function(e) {
// 處理導(dǎo)航點(diǎn)擊
});
// 2. 第三方分析腳本
(function() {
const ga = document.createElement('script');
ga.async = true;
ga.src = 'https://www.google-analytics.com/analytics.js';
document.head.appendChild(ga);
})();
// 3. 非關(guān)鍵功能
function initImageLazyLoading() {
const images = document.querySelectorAll('img[data-src]');
// 懶加載邏輯
}
initImageLazyLoading();
// 4. 社交媒體插件
if (typeof window.FB === 'undefined') {
const fb = document.createElement('script');
fb.src = 'https://connect.facebook.net/en_US/sdk.js';
document.body.appendChild(fb);
}
</script>
</body>
6. 性能優(yōu)化策略
智能加載策略
// 創(chuàng)建動態(tài)腳本加載器
class ScriptLoader {
constructor() {
this.loadedScripts = new Set();
this.loadingScripts = new Map();
}
async loadScript(src, options = {}) {
if (this.loadedScripts.has(src)) {
return Promise.resolve();
}
if (this.loadingScripts.has(src)) {
return this.loadingScripts.get(src);
}
const promise = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = options.async !== false;
script.defer = options.defer || false;
script.onload = () => {
this.loadedScripts.add(src);
this.loadingScripts.delete(src);
resolve();
};
script.onerror = () => {
this.loadingScripts.delete(src);
reject(new Error(`Failed to load script: ${src}`));
};
// 決定插入位置
const target = options.target === 'head' ?
document.head : document.body;
target.appendChild(script);
});
this.loadingScripts.set(src, promise);
return promise;
}
// 批量加載腳本
async loadScripts(scripts) {
const promises = scripts.map(script =>
typeof script === 'string' ?
this.loadScript(script) :
this.loadScript(script.src, script.options)
);
return Promise.all(promises);
}
}
// 使用示例
const loader = new ScriptLoader();
// 頁面加載完成后再加載非關(guān)鍵腳本
document.addEventListener('DOMContentLoaded', async () => {
try {
// 并行加載多個腳本
await loader.loadScripts([
'https://cdn.jsdelivr.net/npm/chart.js',
{ src: 'analytics.js', options: { defer: true } },
'social-widgets.js'
]);
console.log('所有腳本加載完成');
initializeFeatures();
} catch (error) {
console.error('腳本加載失敗:', error);
}
});
條件加載
// 根據(jù)頁面類型條件加載腳本
function loadPageSpecificScripts() {
const currentPage = document.body.dataset.page;
switch (currentPage) {
case 'product':
// 產(chǎn)品頁面特定腳本
loadScript('product-viewer.js');
loadScript('review-system.js');
break;
case 'checkout':
// 結(jié)賬頁面特定腳本
loadScript('payment-processor.js');
loadScript('address-validator.js');
break;
case 'blog':
// 博客頁面特定腳本
loadScript('comment-system.js');
loadScript('social-share.js');
break;
default:
// 通用腳本
loadScript('common-features.js');
}
}
// 根據(jù)用戶交互加載腳本
function loadOnInteraction() {
// 用戶首次交互時加載重型腳本
function loadHeavyScripts() {
loadScript('heavy-animation-library.js');
loadScript('complex-ui-components.js');
// 移除事件監(jiān)聽器,只加載一次
['mousedown', 'touchstart', 'keydown'].forEach(event => {
document.removeEventListener(event, loadHeavyScripts, true);
});
}
['mousedown', 'touchstart', 'keydown'].forEach(event => {
document.addEventListener(event, loadHeavyScripts, true);
});
}
// 頁面加載完成后執(zhí)行
document.addEventListener('DOMContentLoaded', () => {
loadPageSpecificScripts();
loadOnInteraction();
});
7. 最佳實(shí)踐總結(jié)
現(xiàn)代推薦做法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>現(xiàn)代網(wǎng)頁</title>
<!-- 1. 關(guān)鍵CSS內(nèi)聯(lián)或預(yù)加載 -->
<style>/* 關(guān)鍵CSS */</style>
<link rel="preload" href="main.css" rel="external nofollow" as="style">
<!-- 2. 重要配置腳本(最小化) -->
<script>
window.APP_CONFIG = {/* 基礎(chǔ)配置 */};
</script>
<!-- 3. 核心庫使用defer -->
<script src="react.min.js" defer></script>
<script src="main.js" defer></script>
<!-- 4. 分析腳本使用async -->
<script src="analytics.js" async></script>
</head>
<body>
<!-- 頁面內(nèi)容 -->
<div id="root">
<h1>頁面內(nèi)容</h1>
<!-- 其他內(nèi)容 -->
</div>
<!-- 5. 非關(guān)鍵腳本放在底部或動態(tài)加載 -->
<script>
// 延遲加載非關(guān)鍵功能
setTimeout(() => {
import('./non-critical-features.js');
}, 1000);
// 基于用戶交互加載
document.addEventListener('scroll', () => {
import('./scroll-effects.js');
}, { once: true });
</script>
</body>
</html>
使用搭配
| 位置 | 適用場景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|---|
| Head | 關(guān)鍵配置、用戶代理檢測 | 執(zhí)行早、配置及時生效 | 阻塞渲染、延遲首屏 |
| Head + defer | 核心功能庫、框架 | 按順序執(zhí)行、DOM可用 | 需要瀏覽器支持 |
| Head + async | 獨(dú)立分析腳本、廣告 | 不阻塞解析、并行下載 | 執(zhí)行時機(jī)不確定 |
| Body底部 | DOM操作、事件綁定 | 不阻塞渲染、DOM可用 | 可能延遲功能可用時間 |
| 動態(tài)加載 | 非關(guān)鍵功能、條件功能 | 最優(yōu)性能、按需加載 | 實(shí)現(xiàn)復(fù)雜、可能影響SEO |
到此這篇關(guān)于淺析前端JS腳本放在head與body對加載的影響以及優(yōu)化策略的文章就介紹到這了,更多相關(guān)JS腳本位置影響內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS模擬實(shí)現(xiàn)哈希表及應(yīng)用詳解
這篇文章主要介紹了JS模擬實(shí)現(xiàn)哈希表及應(yīng)用,結(jié)合實(shí)例形式分析了javascript模擬實(shí)現(xiàn)哈希表的步驟、相關(guān)操作技巧與使用方法,需要的朋友可以參考下2018-05-05
前端首屏加載速度性能優(yōu)化的實(shí)戰(zhàn)指南
首屏加載速度是衡量網(wǎng)站用戶體驗(yàn)的關(guān)鍵指標(biāo),本文將分享幾個在實(shí)際項(xiàng)目中總結(jié)的性能優(yōu)化方案,幫助你的網(wǎng)站首屏加載速度提升80%,大家可以根據(jù)需要進(jìn)行選擇2025-11-11
javascript中RegExp保留小數(shù)點(diǎn)后幾位數(shù)的方法分享
文章介紹一篇關(guān)于javascript中RegExp保留小數(shù)點(diǎn)后幾位數(shù)方法,有需要了解的朋友可以參考一下2013-08-08
微信小程序基于canvas漸變實(shí)現(xiàn)的彩虹效果示例
這篇文章主要介紹了微信小程序基于canvas漸變實(shí)現(xiàn)的彩虹效果,結(jié)合實(shí)例形式分析了微信小程序線性漸變及圓形漸變的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-05-05
利用paper.js實(shí)現(xiàn)圖片簡單框選標(biāo)注功能
Paper.js是一個 JavaScript庫用來制作繪圖和動畫,這篇文章主要介紹了利用paper.js實(shí)現(xiàn)圖片簡單框選標(biāo)注功能的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-10-10

