👨💻 前端工程师 + 段子手 = 你的专属导师
大家好!今天我们要一起从0到1开发一个电影网站。别慌!我们一步步来,代码界的"复仇者联盟"即将登场!🎬✨

🧱 第一章:表单与默认行为 ------ 用户体验小剧场

技术点:event.preventDefault()
的作用
小白疑问 :
"老师,为什么我一提交表单页面就白屏?难道是浏览器在玩消失?"
老司机解答 :
"不慌!这其实是HTML早期的'传统艺能'------表单默认会刷新页面,就像咖啡机突然断电一样,用户体验一言难尽。😩"
⚙️ 解决方案:event.preventDefault()
javascript
// 防止表单默认提交行为
oForm.addEventListener('submit', (event) => {
event.preventDefault(); // ⚠️ 关键点:阻止页面刷新
const search = oInput.value.trim();
getMovies(search);
});
💡 小技巧:
event.preventDefault()
是前端开发的"急救包",用来拦截表单默认行为。- 类比:就像给咖啡机装了个保险丝,避免它突然断电。
🧩 第二章:HTML5语义化标签 ------ 用乐高搭房子 🏗️

技术点:<header>
、<main>
等标签的意义
小白疑问 :
"为什么不能全用 <div>
?难道 <header>
是个隐藏的彩蛋?"
老司机解答 :
"<header>
、<main>
是HTML5的'乐高积木',结构清晰才能稳如老狗!🐶
<header>
:网站的门面,放搜索框和logo。<main>
:核心内容区,放电影卡片。<script>
:放在底部,避免阻塞页面加载。"
📄 示例代码:HTML结构
html
<header>
<form id="form">
<input
type="text"
id="search"
placeholder="Search"
required
>
</form>
</header>
<main id="main"></main>
<script src="./script.js"></script>
💡 小技巧:
- 语义化标签有助于SEO和无障碍访问,就像给房子装上导航系统。
🧪 第三章:模块化函数 ------ 代码界的乐高 🎮

技术点:showMovies()
、showError()
的封装
小白疑问 :
"为什么要把代码拆成多个函数?难道是为了考试?"
老司机解答 :
"模块化是前端的'乐高哲学'!把代码拆成小块,复用更方便,修改也不怕'牵一发而动全身'。"
🧱 示例代码:模块化函数
javascript
// 显示错误信息
const showError = (message) => {
main.innerHTML = `<div class="error">${message}</div>`;
}
// 获取电影数据
const getMovies = async (keyword) => {
try {
// ...
showMovies(data.results);
} catch (error) {
showError('获取电影数据失败,请稍后重试');
}
}
💡 小技巧:
- 每个函数只做一件事,像乐高积木一样灵活组合。
⚡️ 第四章:防抖函数(Debounce) ------ 键盘上的弹簧侠

技术点:debounce
函数的实现
小白疑问 :
"为什么输入框一打字就疯狂请求API?难道是键盘在健身?💪"
老司机解答 :
"这是'防抖函数'的战场!防抖就像给键盘装弹簧,等用户停下来再请求API,避免频繁抖动。"
🔧 示例代码:防抖函数
javascript
// 防抖函数
const debounce = (func, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(null, args), delay);
};
}
// 添加输入防抖
oInput.addEventListener('input', debounce((e) => {
const search = e.target.value.trim();
if (search) {
getMovies(search);
}
}, 500)); // ⚠️ 延迟500ms
💡 小技巧:
- 防抖常用于搜索框,减少不必要的API请求,节省服务器资源。
🍲 第五章:JSONView插件 ------ 调试API的火锅汤底

技术点:JSONView插件的重要性
小白疑问 :
"为什么我的API返回一团乱码?难道是程序员在说暗语?🤫"
老司机解答 :
"JSONView插件就是你的'火锅汤底',让JSON数据一目了然,调试API像看菜单一样清晰!🍲"
🛠️ 使用建议:
- 安装 JSONView 插件。
- 打开开发者工具(F12),查看API返回的数据结构。
📱 第六章:响应式设计 ------ 网站变身手机小精灵

技术点:CSS媒体查询 + 动画效果
小白疑问 :
"为什么我的网站在手机上看得好丑?难道是代码过敏?📱"
老司机解答 :
"响应式设计就是'手机小精灵',用CSS媒体查询让网站自动适应不同屏幕,动画效果还能加分!"
🌈 示例代码:媒体查询
css
/* 响应式设计 */
@media (max-width: 768px) {
.movie {
width: 280px;
}
.movie img {
height: 400px;
}
}
💡 小技巧:
- 使用
@media
查询适配不同设备,让网站在手机上也能优雅展示。
🎉 结尾:恭喜你召唤了"复仇者联盟"!
AI生图日常离谱,复仇者联盟出来个蝙蝠侠🦇

金句时刻 :
"恭喜你!现在连'复仇者联盟'都能用代码召唤了!🦸♂️🦸♀️
从表单防抖到响应式设计,你已经掌握了前端开发的核心技能!🎉"
下一步:
- 尝试添加更多功能(比如电影详情页、收藏功能)。
- 学习后端知识,让你的电影网站更完整!
🚀 补充:代码片段汇总
📄 index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电影🎦</title>
<link rel="stylesheet" href="./common.css">
</head>
<body>
<header>
<form action="" id="form">
<input
type="text"
id="search"
class="search"
placeholder="Search"
required
>
</form>
</header>
<main id="main"></main>
<script src="./script.js"></script>
</body>
</html>
🧠 script.js
javascript
// 配置信息
const API_URL = 'https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=3fd2be6f0c70a2a598f084ddfb75487c&page=1'
const IMG_PATH = 'https://image.tmdb.org/t/p/w1280'
const SEARCH_API = 'https://api.themoviedb.org/3/search/movie?api_key=3fd2be6f0c70a2a598f084ddfb75487c&query="'
// DOM 元素
const oForm = document.querySelector('#form');
const oInput = document.querySelector('#search');
const main = document.querySelector('#main');
// 显示加载状态
const showLoading = () => {
main.innerHTML = '<div class="loading">加载中...</div>';
}
// 显示错误信息
const showError = (message) => {
main.innerHTML = `<div class="error">${message}</div>`;
}
// 获取电影数据
const getMovies = async (keyword) => {
try {
showLoading();
let reqUrl = keyword ? SEARCH_API + keyword : API_URL;
const response = await fetch(reqUrl);
if (!response.ok) {
throw new Error('网络请求失败');
}
const data = await response.json();
if (data.results.length === 0) {
showError('未找到相关电影');
return;
}
showMovies(data.results);
} catch (error) {
showError('获取电影数据失败,请稍后重试');
console.error('Error:', error);
}
}
// 渲染电影列表
const showMovies = (movies) => {
main.innerHTML = movies.map(movie => {
const {
poster_path,
title,
vote_average,
overview,
release_date
} = movie;
// 处理图片路径
const imgPath = poster_path
? IMG_PATH + poster_path
: 'https://via.placeholder.com/300x450?text=No+Image';
// 处理评分显示
const rating = vote_average.toFixed(1);
const ratingClass = vote_average >= 8 ? 'high-rating' :
vote_average >= 5 ? 'medium-rating' :
'low-rating';
return `
<div class="movie">
<img src="${imgPath}" alt="${title}" loading="lazy">
<div class="movie-info">
<h3>${title}</h3>
<span class="${ratingClass}">${rating}</span>
</div>
<div class="overview">
<h3>${title}</h3>
<p class="release-date">上映日期: ${release_date || '未知'}</p>
<p class="rating">评分: ${rating}</p>
<p class="description">${overview || '暂无简介'}</p>
</div>
</div>
`;
}).join('');
}
// 防抖函数
const debounce = (func, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(null, args), delay);
};
}
// 搜索处理函数
const handleSearch = (event) => {
event.preventDefault();
const search = oInput.value.trim();
if (search) {
getMovies(search);
} else {
getMovies(); // 如果搜索框为空,显示热门电影
}
}
// 页面加载完成后执行
window.addEventListener('load', () => {
getMovies();
});
// 添加搜索事件监听
oForm.addEventListener('submit', handleSearch);
// 添加输入防抖
oInput.addEventListener('input', debounce((e) => {
const search = e.target.value.trim();
if (search) {
getMovies(search);
}
}, 500));
🎨 common.css
css
/* 全局样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background-color: #f0f8ff;
color: #333;
}
/* 头部搜索区域样式 */
header {
background-color: #4a90e2;
padding: 1rem;
display: flex;
justify-content: center;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
#form {
width: 100%;
max-width: 500px;
}
.search {
width: 100%;
padding: 0.8rem 1.5rem;
border: none;
border-radius: 25px;
background-color: #fff;
font-size: 1rem;
transition: all 0.3s ease;
}
.search:focus {
outline: none;
box-shadow: 0 0 5px rgba(74, 144, 226, 0.5);
}
/* 主要内容区域样式 */
main {
display: flex;
justify-content: center;
flex-wrap: wrap;
padding: 2rem;
gap: 2rem;
}
/* 电影卡片样式 */
.movie {
width: 300px;
margin: 1rem;
position: relative;
background-color: #fff;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 3px 10px rgba(0,0,0,0.1);
transition: transform 0.3s ease;
animation: fadeIn 0.5s ease-out;
}
.movie:hover {
transform: translateY(-5px);
}
.movie img {
width: 100%;
height: 450px;
object-fit: cover;
}
.movie-info {
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
}
.movie-info h3 {
font-size: 1.1rem;
color: #2c3e50;
}
.movie-info span {
background-color: #4a90e2;
color: #fff;
padding: 0.3rem 0.6rem;
border-radius: 3px;
font-weight: bold;
}
/* 电影简介样式 */
.overview {
background-color: rgba(255, 255, 255, 0.95);
padding: 2rem;
position: absolute;
left: 0;
bottom: 0;
right: 0;
min-height: 100%;
transform: translateY(101%);
overflow-y: auto;
transition: transform 0.3s ease-in;
}
.movie:hover .overview {
transform: translateY(0);
}
/* 响应式设计 */
@media (max-width: 768px) {
.movie {
width: 280px;
}
.movie img {
height: 400px;
}
}
/* 加载状态样式 */
.loading {
width: 100%;
text-align: center;
padding: 2rem;
font-size: 1.2rem;
color: #4a90e2;
}
/* 错误提示样式 */
.error {
width: 100%;
text-align: center;
padding: 2rem;
color: #e74c3c;
font-size: 1.2rem;
}
/* 评分样式 */
.high-rating {
background-color: #2ecc71 !important;
}
.medium-rating {
background-color: #f1c40f !important;
}
.low-rating {
background-color: #e74c3c !important;
}
/* 电影简介中的日期和评分样式 */
.release-date, .rating {
color: #666;
margin-bottom: 0.5rem;
font-size: 0.9rem;
}
.description {
line-height: 1.6;
color: #333;
}
/* 添加滚动条样式 */
.overview::-webkit-scrollbar {
width: 8px;
}
.overview::-webkit-scrollbar-track {
background: #f1f1f1;
}
.overview::-webkit-scrollbar-thumb {
background: #4a90e2;
border-radius: 4px;
}
.overview::-webkit-scrollbar-thumb:hover {
background: #357abd;
}
/* 添加动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
✅ 技术点覆盖清单
技术点 | 覆盖情况 |
---|---|
表单与默认行为 | ✅ |
HTML5语义化标签 | ✅ |
模块化函数 | ✅ |
防抖函数 | ✅ |
JSONView插件 | ✅ |
响应式设计 | ✅ |
🌟 最后的话
"从0到1"的旅程结束了,但你的代码冒险才刚刚开始!现在你可以自信地告诉朋友:"我用JavaScript召唤了复仇者联盟!" Avengers, assemble! 🦸♂️🦸♀️
如果觉得这篇文章有用,记得点赞、收藏、转发,让更多小白一起加入前端开发的魔法世界!✨