只请求和更新页面中变化的部分,而不是重新加载整个页面。

下面我将从核心原理、详细步骤、代码示例(使用原生JavaScript和jQuery两种方式)以及注意事项四个方面,为你详细讲解如何实现。
核心原理
想象一个普通的网页请求流程:
- 用户点击链接。
- 浏览器向服务器发送一个全新的HTML请求。
- 服务器返回一个完整的、包含
<head>、<nav>、<main>、<footer>等所有标签的HTML页面。 - 浏览器丢弃旧页面,用新页面完全替换。
而使用AJAX的流程是这样的:
- 用户点击导航条中的链接。
- JavaScript 拦截这个默认的页面跳转行为(
e.preventDefault())。 - JavaScript 使用
XMLHttpRequest(原生) 或$.ajax()/fetch()(现代/库) 等技术,只向服务器请求目标页面的内容部分(比如一个HTML片段)。 - 服务器只返回这个内容片段(
<article>...</article>)。 - JavaScript 收到这个内容片段后,只更新到页面中指定的容器(一个
<div id="content">)里。 - 导航条和其他部分因为没有被触及,所以保持不变。
关键点:

- 分离关注点:将导航(结构)和内容(数据)分离,导航是静态的“骨架”,内容是动态加载的“血肉”。
- 服务器端配合:服务器需要能够提供“纯内容”的响应,而不是完整的页面,这通常通过后端路由来实现。
详细实现步骤
步骤 1:HTML 结构准备
你的HTML需要有明确的“静态部分”和“动态部分”。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">AJAX 导航示例</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- 1. 静态导航条 - 这部分将保持不变 -->
<nav id="main-nav">
<ul>
<li><a href="home.html" class="nav-link active">首页</a></li>
<li><a href="about.html" class="nav-link">关于我们</a></li>
<li><a href="services.html" class="nav-link">服务</a></li>
<li><a href="contact.html" class="nav-link">联系我们</a></li>
</ul>
</nav>
<!-- 2. 动态内容容器 - 这是AJAX将要更新的区域 -->
<main id="content-container">
<!-- 初始内容,可以是首页内容或一个加载提示 -->
<p>欢迎来到首页!点击上方导航查看内容变化。</p>
</main>
<!-- 3. 引入AJAX库(可选,但推荐) -->
<!-- <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> -->
<script src="app.js"></script>
</body>
</html>
注意:
- 导航链接的
href属性指向实际的页面URL(如home.html),这对于SEO(搜索引擎优化)和无JavaScript环境下的降级非常重要。 - 给链接添加一个共同的类名(如
nav-link)方便JavaScript选择器操作。
步骤 2:服务器端准备
你的服务器需要能够处理这些请求,这里有两种主要模式:
模式A:后端路由(推荐) 这是最专业的方式,你的后端框架(如 Node.js/Express, PHP, Python/Django, Ruby on Rails)根据URL返回不同的内容。

- 请求
https://yourdomain.com/about - Express.js (Node.js) 示例:
app.get('/about', (req, res) => { // 直接返回关于页面的HTML片段 res.send('<h1>关于我们</h1><p>我们是一家充满活力的公司...</p>'); }); - PHP 示例:
// about.php echo '<h1>关于我们</h1><p>我们是一家充满活力的公司...</p>';
模式B:前端代理(简单演示) 对于纯静态页面,你可以创建一个JSON文件来模拟服务器响应。
content.json
{
"home.html": "<h1>首页</h1><p>这是首页的内容,欢迎您的光临。</p>",
"about.html": "<h1>关于我们</h1><p>我们是一家专注于Web开发的公司。</p>",
"services.html": "<h1>我们的服务</h1><ul><li>网站设计</li><li>应用开发</li><li>技术咨询</li></ul>",
"contact.html": "<h1>联系我们</h1><p>邮箱: contact@example.com</p>"
}
这种方式不适用于生产环境,但有助于理解原理。
JavaScript 实现
现在是最关键的部分:编写AJAX逻辑。
使用原生 JavaScript (现代 fetch API)
fetch 是现代浏览器内置的API,语法简洁,是推荐的原生方法。
app.js
document.addEventListener('DOMContentLoaded', () => {
// 1. 获取所有导航链接和内容容器
const navLinks = document.querySelectorAll('.nav-link');
const contentContainer = document.getElementById('content-container');
// 2. 为每个链接添加点击事件监听器
navLinks.forEach(link => {
link.addEventListener('click', (event) => {
// 3. 阻止链接的默认跳转行为
event.preventDefault();
// 4. 获取要加载的页面URL
const url = link.getAttribute('href');
// 5. 使用 fetch 发送 AJAX 请求
// 这里我们假设服务器返回的是纯HTML片段
fetch(url)
.then(response => {
// 检查响应是否成功
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.text(); // 将响应体作为文本获取
})
.then(html => {
// 6. 获取成功后,将HTML内容插入到容器中
contentContainer.innerHTML = html;
// (可选) 更新活动链接的样式
updateActiveLink(link);
})
.catch(error => {
// 7. 处理请求错误
console.error('Error fetching content:', error);
contentContainer.innerHTML = '<p>抱歉,加载内容时出错。</p>';
});
});
});
// 辅助函数:更新活动链接的样式
function updateActiveLink(activeLink) {
navLinks.forEach(link => link.classList.remove('active'));
activeLink.classList.add('active');
}
});
使用 jQuery
如果你项目已经引入了jQuery,代码会更简洁。
app.js
$(document).ready(function() {
// 1. 获取所有导航链接和内容容器
var $navLinks = $('.nav-link');
var $contentContainer = $('#content-container');
// 2. 为每个链接添加点击事件监听器
$navLinks.on('click', function(event) {
// 3. 阻止链接的默认跳转行为
event.preventDefault();
// 4. 获取要加载的页面URL
var url = $(this).attr('href');
// 5. 使用 jQuery 的 $.ajax 发送请求
$.ajax({
url: url,
type: 'GET', // 请求方法
dataType: 'html', // 期望服务器返回的数据类型
// beforeSend: function() {
// $contentContainer.html('<p>加载中...</p>'); // 可选:显示加载状态
// },
success: function(html) {
// 6. 请求成功,更新内容
$contentContainer.html(html);
// (可选) 更新活动链接的样式
updateActiveLink($(this));
},
error: function(xhr, status, error) {
// 7. 请求失败,处理错误
console.error("Error: " + error);
$contentContainer.html('<p>抱歉,加载内容时出错。</p>');
}
});
});
// 辅助函数:更新活动链接的样式
function updateActiveLink($activeLink) {
$navLinks.removeClass('active');
$activeLink.addClass('active');
}
});
高级优化与注意事项
-
加载状态指示器加载完成前,给用户一个反馈(比如一个旋转的图标或“加载中...”文字),可以提升用户体验。
// 在 fetch 的 .then 之前 contentContainer.innerHTML = '<div class="loader">加载中...</div>'; // 在 fetch 的 .then 成功回调里,替换掉 loader contentContainer.innerHTML = html;
-
浏览器前进/后退按钮 (History API) 这是实现单页应用体验的关键一步,我们点击链接,URL不会变,用户无法使用浏览器的前进/后退按钮。
history.pushState(): 当你通过AJAX加载新内容时,调用此方法来更新浏览器的地址栏URL,但不会刷新页面。window.onpopstate: 监听浏览器的前进/后退事件,当用户点击后退时,根据当前URL重新加载对应的内容。
示例 (结合
fetch):// 在点击事件处理函数中,fetch 成功后添加 history.pushState({ page: url }, '', url); // 更新URL // 在全局添加 popstate 监听器 window.addEventListener('popstate', (event) => { // 当用户点击前进/后退时,event.state 会保存之前 pushState 的数据 const url = event.state ? event.state.page : 'home.html'; // 默认首页 fetch(url) .then(response => response.text()) .then(html => { contentContainer.innerHTML = html; // ... 更新活动链接等 }); }); -
SEO 考量
- 优点:由于初始HTML中包含了完整的导航和链接,搜索引擎爬虫仍然可以索引到你的网站结构。
- 缺点:AJAX加载的内容是动态生成的,搜索引擎可能无法很好地抓取这些内容,对于SEO要求极高的网站,传统的服务器端渲染仍然是更稳妥的选择。
-
书签功能 由于URL被
History API正确更新,用户刷新页面或直接访问某个URL(如yourdomain.com/about)时,页面应该能正确显示对应的内容,这需要你的服务器在收到这类请求时,返回完整的HTML页面(包含导航和内容),而不是仅返回内容片段,这被称为同构渲染或渐进式增强。
实现“导航条不变,网页内容改变”的核心流程可以概括为:
- HTML结构分离:静态导航 + 动态内容容器。
- JS事件拦截:监听导航点击,
preventDefault()阻止默认跳转。 - AJAX请求内容:使用
fetch或$.ajax向服务器请求目标页面的HTML片段。 - DOM更新内容:将获取到的HTML片段插入到内容容器中。
- 优化用户体验:结合 History API 实现URL同步,添加加载状态指示器。
通过这种方式,你可以创建出响应迅速、体验流畅的现代化网站。
