<think>我们使用Navigo作为路由库,结合ES6语法和HTML5构建一个单页面应用(SPA)。下面是一个详细的示例,包括以下功能:
1. 使用Navigo处理路由
2. ES6模块化组织代码
3. 动态加载页面内容
4. 导航菜单
5. 页面切换动画
项目结构:
index.html
main.js (入口文件)
router.js (路由配置)
views/ (页面组件)
home.js
about.js
contact.js
步骤:
1. 创建HTML文件
2. 安装Navigo(通过CDN或npm)
3. 编写路由配置
4. 编写页面组件(使用ES6类)
5. 实现页面切换逻辑
注意:为了简化,我们不使用打包工具,直接在浏览器中使用ES6模块。
开始实现:</think>### 基于HTML5、Navigo路由和ES6的Web应用开发示例
下面是一个完整的单页面应用(SPA)示例,使用原生HTML5、Navigo路由库和ES6语法实现:
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Navigo路由示例</title>
<script src="https://unpkg.com/navigo@8.11.1/lib/navigo.min.js"></script>
<style>
:root {
--primary-color: #3498db;
--text-color: #333;
--border-radius: 8px;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', sans-serif;
line-height: 1.6;
color: var(--text-color);
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
padding: 20px;
}
header {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
padding: 20px;
border-radius: var(--border-radius);
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
nav ul {
display: flex;
list-style: none;
gap: 15px;
}
nav a {
text-decoration: none;
color: var(--text-color);
padding: 8px 15px;
border-radius: 20px;
transition: all 0.3s ease;
}
nav a:hover, nav a.active {
background: var(--primary-color);
color: white;
}
main {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border-radius: var(--border-radius);
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
padding: 30px;
min-height: 60vh;
}
.page {
display: none;
}
.page.active {
display: block;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}
.product-card {
background: white;
border-radius: var(--border-radius);
overflow: hidden;
box-shadow: 0 3px 10px rgba(0,0,0,0.1);
transition: transform 0.3s;
}
.product-card:hover {
transform: translateY(-5px);
}
.product-image {
width: 100%;
height: 180px;
object-fit: cover;
}
button {
background: var(--primary-color);
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin-top: 10px;
transition: background 0.3s;
}
button:hover {
background: #2980b9;
}
.form-group {
margin-bottom: 20px;
}
input, textarea {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
footer {
text-align: center;
margin-top: 30px;
color: #666;
}
/* 响应式设计 */
@media (max-width: 768px) {
header {
flex-direction: column;
text-align: center;
}
nav ul {
margin-top: 15px;
flex-wrap: wrap;
justify-content: center;
}
.product-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<header>
<h1>Navigo路由示例</h1>
<nav>
<ul>
<li><a href="/" data-navigo>首页</a></li>
<li><a href="/products" data-navigo>产品</a></li>
<li><a href="/contact" data-navigo>联系我们</a></li>
<li><a href="/products/1" data-navigo>产品详情</a></li>
</ul>
</nav>
</header>
<main id="app">
<!-- 页面内容由JavaScript动态渲染 -->
</main>
<footer>
<p>© 2023 HTML5 SPA示例 | 使用Navigo路由库</p>
</footer>
<script type="module">
// ES6模块化组织代码
import { ProductService } from './services.js';
// 初始化Navigo路由器
const root = '/';
const router = new Navigo(root, { linksSelector: "a[data-navigo]" });
// 页面组件
class HomePage {
render() {
return `
<section class="page active" id="home">
<h2>欢迎使用我们的产品</h2>
<p>这是一个使用Navigo路由库和ES6语法的单页面应用示例</p>
<p>主要功能:</p>
<ul>
<li>基于Navigo的客户端路由</li>
<li>ES6类组件组织</li>
<li>动态内容加载</li>
<li>响应式设计</li>
</ul>
<button id="loadProducts">加载产品</button>
</section>
`;
}
afterRender() {
document.getElementById('loadProducts').addEventListener('click', () => {
router.navigate('/products');
});
}
}
class ProductsPage {
async render() {
const products = await ProductService.fetchProducts();
return `
<section class="page" id="products">
<h2>产品列表</h2>
<div class="product-grid">
${products.map(product => `
<div class="product-card">
<img src="${product.image}" alt="${product.name}" class="product-image">
<div class="product-info">
<h3>${product.name}</h3>
<p>${product.description}</p>
<p class="price">¥${product.price}</p>
<a href="/products/${product.id}" data-navigo>查看详情</a>
</div>
</div>
`).join('')}
</div>
</section>
`;
}
}
class ProductDetailPage {
async render(params) {
const productId = params.id;
const product = await ProductService.getProductById(productId);
if (!product) {
return `
<section class="page" id="product-detail">
<h2>产品不存在</h2>
<p>请求的产品ID: ${productId} 不存在</p>
<a href="/products" data-navigo>返回产品列表</a>
</section>
`;
}
return `
<section class="page" id="product-detail">
<div style="display: flex; gap: 30px; flex-wrap: wrap;">
<div style="flex: 1; min-width: 300px;">
<img src="${product.image}" alt="${product.name}" style="width: 100%; border-radius: 8px;">
</div>
<div style="flex: 2; min-width: 300px;">
<h2>${product.name}</h2>
<p>${product.description}</p>
<p><strong>价格:</strong> ¥${product.price}</p>
<p><strong>库存:</strong> ${product.stock}件</p>
<button id="addToCart">加入购物车</button>
<a href="/products" data-navigo style="display: inline-block; margin-left: 10px;">返回列表</a>
</div>
</div>
</section>
`;
}
afterRender() {
document.getElementById('addToCart')?.addEventListener('click', () => {
alert('已加入购物车!');
});
}
}
class ContactPage {
render() {
return `
<section class="page" id="contact">
<h2>联系我们</h2>
<form id="contact-form">
<div class="form-group">
<input type="text" placeholder="姓名" required>
</div>
<div class="form-group">
<input type="email" placeholder="邮箱" required>
</div>
<div class="form-group">
<textarea placeholder="留言" rows="5" required></textarea>
</div>
<button type="submit">提交</button>
</form>
</section>
`;
}
afterRender() {
document.getElementById('contact-form').addEventListener('submit', (e) => {
e.preventDefault();
alert('表单已提交,我们会尽快联系您!');
e.target.reset();
});
}
}
class NotFoundPage {
render() {
return `
<section class="page active" id="not-found">
<h2>404 - 页面未找到</h2>
<p>请求的页面不存在,请检查URL或返回<a href="/" data-navigo>首页</a></p>
</section>
`;
}
}
// 路由配置
router
.on({
'/': async () => {
const page = new HomePage();
document.getElementById('app').innerHTML = page.render();
page.afterRender();
},
'/products': async () => {
const page = new ProductsPage();
document.getElementById('app').innerHTML = await page.render();
},
'/products/:id': async ({ data }) => {
const page = new ProductDetailPage();
document.getElementById('app').innerHTML = await page.render(data);
page.afterRender();
},
'/contact': () => {
const page = new ContactPage();
document.getElementById('app').innerHTML = page.render();
page.afterRender();
}
})
.notFound(() => {
const page = new NotFoundPage();
document.getElementById('app').innerHTML = page.render();
})
.resolve();
// 添加路由钩子
router.hooks({
before: (done, match) => {
console.log(`即将导航到: ${match.url}`);
// 可以在这里添加权限检查
done();
},
after: (match) => {
console.log(`导航完成: ${match.url}`);
// 更新导航菜单活动状态
document.querySelectorAll('nav a').forEach(link => {
link.classList.toggle('active', link.getAttribute('href') === match.url);
});
}
});
</script>
</body>
</html>
```
### 服务模块 (services.js)
```javascript
// services.js - ES6模块提供数据服务
export class ProductService {
static async fetchProducts() {
// 模拟API请求
return new Promise(resolve => {
setTimeout(() => {
resolve([
{ id: 1, name: "高级解决方案", description: "专业级产品", price: 299, image: "https://via.placeholder.com/300?text=产品1" },
{ id: 2, name: "标准版", description: "适合中小企业", price: 199, image: "https://via.placeholder.com/300?text=产品2" },
{ id: 3, name: "入门版", description: "个人用户最佳选择", price: 99, image: "https://via.placeholder.com/300?text=产品3" }
]);
}, 300);
});
}
static async getProductById(id) {
const products = await this.fetchProducts();
return products.find(p => p.id === parseInt(id));
}
}
```
### 关键技术实现
1. **Navigo路由配置**
```javascript
const router = new Navigo(root, { linksSelector: "a[data-navigo]" });
router.on({
'/': () => renderHome(),
'/products': () => renderProducts(),
'/products/:id': ({ data }) => renderProductDetail(data.id)
});
```
2. **ES6类组件**
```javascript
class HomePage {
render() {
return `<section>...</section>`;
}
afterRender() {
// DOM操作和事件绑定
}
}
```
3. **动态路由参数处理**
```javascript
router.on('/products/:id', async ({ data }) => {
const product = await ProductService.getProductById(data.id);
});
```
4. **路由生命周期钩子**
```javascript
router.hooks({
before: (done, match) => {
// 路由前检查
done();
},
after: (match) => {
// 路由后更新UI
}
});
```
5. **模块化服务**
```javascript
export class ProductService {
static async fetchProducts() {
// API请求模拟
}
}
```
### 路由性能优化策略
1. **代码分割与懒加载**
```javascript
// 动态导入组件
router.on('/products', () => {
import('./ProductsPage.js').then(module => {
const page = new module.default();
document.getElementById('app').innerHTML = page.render();
});
});
```
2. **路由预取**
```javascript
// 鼠标悬停时预加载
document.querySelectorAll('a[data-navigo]').forEach(link => {
link.addEventListener('mouseover', () => {
const path = link.getAttribute('href');
import(`./${path.split('/')[1]}Page.js`);
});
});
```
3. **过渡动画优化**
```css
.page {
transition: opacity 0.3s, transform 0.3s;
}
.page-leaving {
opacity: 0;
transform: translateY(-10px);
}
.page-entering {
opacity: 1;
transform: translateY(0);
}
```
### 原生应用集成方案
要将此SPA转换为原生应用,可以使用以下方案:
1. **Capacitor集成**
```bash
npm install @capacitor/core @capacitor/cli
npx cap init
npx cap add android
npx cap add ios
```
2. **Cordova打包**
```bash
npm install -g cordova
cordova create myapp
cordova platform add android
cordova platform add ios
```
3. **PWA增强**
```json
// manifest.json
{
"name": "SPA示例",
"short_name": "SPA",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#3498db"
}
```
### 相关问题
1. **Navigo如何处理嵌套路由?**
> Navigo支持嵌套路由配置,可以通过`on()`方法的嵌套实现多级路由结构,例如`/admin/users/:id`[^1]。
2. **ES6模块在旧版浏览器如何兼容?**
> 使用Babel转译和Webpack打包,或通过`<script type="module">`与`<script nomodule>`双方案提供回退支持[^3]。
3. **如何实现路由过渡动画?**
> 在路由钩子中为离开页面添加`.page-leaving`类,为新页面添加`.page-entering`类,配合CSS过渡实现动画效果[^3]。
4. **Navigo与React/Vue路由器有何区别?**
> Navigo是轻量级无框架路由器,而React Router/Vue Router深度集成各自框架,提供组件化路由方案[^1][^3]。
5. **如何优化SPA的SEO?**
> 使用预渲染(Prerender SPA Plugin)、服务端渲染(SSR)或动态渲染(Dynamic Rendering)技术解决SPA的SEO问题[^3]。
6. **Navigo如何支持HTML5 History模式?**
> 初始化时设置`useHash: false`选项,服务器需配置URL重写规则指向入口文件[^1][^4]。