🎬 从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! 🦸‍♂️🦸‍♀️

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

相关推荐
lixin1 分钟前
使用 MCP 协议扩展 Cursor 功能:原理解析与实战指南
前端
a cool fish(无名)11 分钟前
rust-模块树中引用项的路径
java·前端·rust
前端进阶者21 分钟前
天地图Marker跳一跳动画
前端
火柴就是我24 分钟前
每日见闻之Three.js 根据官方demo 理解相机位置
前端
JosieBook34 分钟前
【web应用】基于Vue3和Spring Boot的课程管理前后端数据交互过程
前端·spring boot·交互
刘大猫.41 分钟前
npm ERR! cb() never called!
前端·npm·node.js·npm install·npmm err·never called
咔咔一顿操作1 小时前
常见问题三
前端·javascript·vue.js·前端框架
前端程序媛Ying1 小时前
点击按钮滚动到底功能vue的v-on:scroll运用
javascript
上单带刀不带妹1 小时前
Web Worker:解锁浏览器多线程,提升前端性能与体验
前端·js·web worke
电商API大数据接口开发Cris1 小时前
Node.js + TypeScript 开发健壮的淘宝商品 API SDK
前端·数据挖掘·api