🎬 从0到1开发一个电影网站:前端小白的实战指南 🚀

👨‍💻 前端工程师 + 段子手 = 你的专属导师

大家好!今天我们要一起从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! 🦸‍♂️🦸‍♀️

如果觉得这篇文章有用,记得点赞、收藏、转发,让更多小白一起加入前端开发的魔法世界!✨

相关推荐
天生我材必有用_吴用4 分钟前
深入理解JavaScript设计模式之策略模式
前端
海上彼尚6 分钟前
Vue3 PC端 UI组件库我更推荐Naive UI
前端·vue.js·ui
述雾学java7 分钟前
Vue 生命周期详解(重点:mounted)
前端·javascript·vue.js
洛千陨12 分钟前
Vue实现悬浮图片弹出大图预览弹窗,弹窗顶部与图片顶部平齐
前端·vue.js
咚咚咚ddd14 分钟前
微前端第四篇:qiankun老项目渐进式升级方案(jQuery + React)
前端·前端工程化
螃蟹82717 分钟前
作用域下的方法如何调用?
前端
独立开阀者_FwtCoder20 分钟前
TypeScript 杀疯了,开发 AI 应用新趋势!
前端·javascript·github
汪子熙25 分钟前
QRCode.js:一款轻量级、跨浏览器的 JavaScript 二维码生成库
前端·javascript·面试
Mintopia26 分钟前
Three.js 阴影映射:光影魔术师的神秘配方
前端·javascript·three.js
sztomarch27 分钟前
Router-Routing
linux·运维·服务器·前端·网络