前端开发常见代码片段

✨ 前端开发常见代码片段精选 ✨

您好!我作为一名资深的前端架构师和代码精益求精的专家,将为您精心整理和展示前端开发中那些不可或缺、高频使用且极具实用价值的代码片段。 这些片段涵盖了从基础操作到高级技巧,旨在帮助您提升开发效率和代码质量。

一、HTML 结构与语义

HTML 是网页内容的骨架,语义化的 HTML 有助于提高可访问性、SEO 和代码可维护性。

1.1 页面基础模板

一个标准的 HTML5 页面模板,包含必要的元信息和结构。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>我的前端页面</title>
    <link rel="stylesheet" href="styles.css"> <!-- 引入外部 CSS 文件 -->
</head>
<body>
    <header>
        <h1>页面标题</h1>
        <nav>
            <ul>
                <li><a href="#">首页</a></li>
                <li><a href="#">关于我们</a></li>
                <li><a href="#">联系我们</a></li>
            </ul>
        </nav>
    </header>

    <main>
        <section>
            <h2>主要内容区域</h2>
            <p>这里是页面的核心内容。</p>
        </section>

        <aside>
            <h3>侧边栏</h3>
            <p>一些补充信息或广告。</p>
        </aside>
    </main>

    <footer>
        <p>&copy; 2023 我的公司. 版权所有.</p>
    </footer>

    <script src="main.js"></script> <!-- 引入外部 JavaScript 文件 -->
</body>
</html>

1.2 常用表单元素

构建用户交互的核心。

<form action="/submit" method="post">
    <label for="username">用户名:</label>
    <input type="text" id="username" name="username" placeholder="请输入用户名" required><br><br>

    <label for="password">密码:</label>
    <input type="password" id="password" name="password" minlength="6"><br><br>

    <label for="email">邮箱:</label>
    <input type="email" id="email" name="email"><br><br>

    <label for="gender">性别:</label>
    <input type="radio" id="male" name="gender" value="male">
    <label for="male">男</label>
    <input type="radio" id="female" name="gender" value="female">
    <label for="female">女</label><br><br>

    <label for="interests">兴趣:</label>
    <input type="checkbox" id="reading" name="interests" value="reading">
    <label for="reading">阅读</label>
    <input type="checkbox" id="sports" name="interests" value="sports">
    <label for="sports">运动</label><br><br>

    <label for="country">国家:</label>
    <select id="country" name="country">
        <option value="" disabled selected>请选择国家</option>
        <option value="china">中国</option>
        <option value="usa">美国</option>
        <option value="japan">日本</option>
    </select><br><br>

    <label for="message">留言:</label>
    <textarea id="message" name="message" rows="5" cols="30"></textarea><br><br>

    <input type="submit" value="提交">
    <button type="reset">重置</button>
</form>

二、CSS 常用布局与样式

CSS 负责页面的美化和布局,掌握常用的 CSS 技巧能让您的页面更具表现力。

2.1 Flexbox 布局

弹性盒模型是现代前端布局的利器,非常适合构建响应式和动态排列的元素。

/* 父容器 */
.flex-container {
    display: flex; /* 启用 Flexbox */
    justify-content: space-between; /* 子元素在主轴上均匀分布,两端对齐 */
    align-items: center; /* 子元素在交叉轴上居中对齐 */
    flex-wrap: wrap; /* 子元素如果空间不足,允许换行 */
    gap: 10px; /* 子元素之间的间距 (CSS Gap 属性) */
    height: 200px;
    border: 1px solid #ccc;
    padding: 10px;
}

/* 子元素 */
.flex-item {
    width: 80px;
    height: 80px;
    background-color: #3498db;
    color: white;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 1.2em;
}

2.2 Grid 布局

CSS Grid 提供了强大的二维网格布局能力,适合复杂的页面结构。

/* 父容器 */
.grid-container {
    display: grid; /* 启用 Grid 布局 */
    grid-template-columns: 1fr 2fr 1fr; /* 定义三列,比例为 1:2:1 */
    grid-template-rows: auto 100px; /* 定义两行,第一行高度自适应,第二行固定 100px */
    gap: 15px; /* 行和列之间的间距 */
    width: 100%;
    height: 300px;
    border: 1px solid #ccc;
    padding: 10px;
}

/* 子元素可以放置在特定网格区域 */
.grid-item-1 {
    grid-column: 1 / 3; /* 占据第 1 列到第 3 列 (跨越两列) */
    grid-row: 1; /* 占据第 1 行 */
    background-color: #27ae60;
    color: white;
    padding: 10px;
}
.grid-item-2 {
    grid-column: 3;
    grid-row: 1 / 3; /* 占据第 1 行到第 3 行 (跨越两行) */
    background-color: #e67e22;
    color: white;
    padding: 10px;
}
.grid-item-3 {
    grid-column: 1;
    grid-row: 2;
    background-color: #8e44ad;
    color: white;
    padding: 10px;
}
.grid-item-4 {
    grid-column: 2;
    grid-row: 2;
    background-color: #c0392b;
    color: white;
    padding: 10px;
}

2.3 响应式设计 - 媒体查询

让您的网页在不同设备上都能良好显示。

/* 默认样式 (适用于大屏幕) */
.my-element {
    width: 800px;
    font-size: 18px;
    padding: 20px;
    background-color: lightblue;
}

/* 当屏幕宽度小于等于 768px 时应用 */
@media (max-width: 768px) {
    .my-element {
        width: 100%; /* 全宽 */
        font-size: 16px;
        padding: 15px;
        background-color: lightcoral;
    }
}

/* 当屏幕宽度小于等于 480px 时应用 */
@media (max-width: 480px) {
    .my-element {
        font-size: 14px;
        padding: 10px;
        background-color: lightgreen;
    }
}

2.4 垂直居中技巧

经典的 CSS 挑战之一,这里提供两种常用方法:Flexbox 和绝对定位+transform。

/* 方法一:使用 Flexbox (推荐) */
.parent-flex {
    display: flex;
    justify-content: center; /* 水平居中 */
    align-items: center; /* 垂直居中 */
    height: 300px; /* 父元素需有固定高度 */
    border: 1px dashed #999;
}

.child-flex {
    width: 100px;
    height: 100px;
    background-color: #f39c12;
    color: white;
    text-align: center;
    line-height: 100px; /* 单行文本垂直居中 */
}

/* 方法二:使用绝对定位 + transform */
.parent-absolute {
    position: relative; /* 父元素需要定位 */
    height: 300px;
    border: 1px dashed #999;
}

.child-absolute {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%); /* 自身宽高的一半进行偏移 */
    width: 100px;
    height: 100px;
    background-color: #1abc9c;
    color: white;
    text-align: center;
    line-height: 100px;
}

三、JavaScript 常用功能

JavaScript 是前端的灵魂,它赋予页面交互能力。以下是一些高频使用的 JS 片段。

3.1 DOM 操作

获取元素、修改内容、添加/删除类名、事件监听等。

// 1. 获取元素
const myButton = document.getElementById('myButton'); // 按 ID 获取
const paragraphs = document.getElementsByClassName('my-paragraph'); // 按类名获取 (HTMLCollection)
const firstParagraph = document.querySelector('.my-paragraph'); // 获取第一个匹配的元素
const allLinks = document.querySelectorAll('a'); // 获取所有匹配的元素 (NodeList)

// 2. 修改元素内容和属性
if (firstParagraph) {
    firstParagraph.textContent = '新的段落内容。'; // 修改文本内容
    firstParagraph.innerHTML = '<strong>更强的</strong>内容。'; // 修改 HTML 内容
    firstParagraph.setAttribute('data-info', 'important'); // 设置自定义属性
    firstParagraph.style.color = 'blue'; // 修改内联样式
}

// 3. 添加/删除/切换类名
const myDiv = document.getElementById('myDiv');
if (myDiv) {
    myDiv.classList.add('active'); // 添加类
    myDiv.classList.remove('inactive'); // 移除类
    myDiv.classList.toggle('highlight'); // 切换类 (存在则移除,不存在则添加)
    console.log(myDiv.classList.contains('active')); // 检查是否包含类
}

// 4. 事件监听
if (myButton) {
    myButton.addEventListener('click', function() {
        alert('按钮被点击了!');
        console.log('按钮点击事件触发');
    });

    // 移除事件监听 (如果需要)
    // function handleClick() { alert('点击'); }
    // myButton.addEventListener('click', handleClick);
    // myButton.removeEventListener('click', handleClick);
}

// 5. 创建和插入元素
const newDiv = document.createElement('div');
newDiv.textContent = '这是一个新创建的 div。';
newDiv.classList.add('new-element');
document.body.appendChild(newDiv); // 添加到 body 的末尾

const container = document.getElementById('container');
if (container) {
    const existingElement = document.getElementById('existing');
    container.insertBefore(newDiv, existingElement); // 在 existingElement 之前插入
}

// 6. 删除元素
const elementToRemove = document.getElementById('toRemove');
if (elementToRemove && elementToRemove.parentNode) {
    elementToRemove.parentNode.removeChild(elementToRemove);
}

3.2 数据处理与数组操作

前端经常需要处理数据,数组是其中最常见的数据结构。

// 1. 遍历数组
const numbers = [1, 2, 3, 4, 5];

numbers.forEach(function(num, index) {
    console.log(`索引 ${index} 的值是: ${num}`);
});

// 2. 映射 (map): 创建一个新数组,其每个元素是原数组对应元素执行回调函数后的结果
const doubledNumbers = numbers.map(num => num * 2); // [2, 4, 6, 8, 10]
console.log('翻倍后的数字:', doubledNumbers);

// 3. 过滤 (filter): 创建一个新数组,包含通过测试的元素
const evenNumbers = numbers.filter(num => num % 2 === 0); // [2, 4]
console.log('偶数:', evenNumbers);

// 4. 查找 (find/findIndex): 查找第一个符合条件的元素或其索引
const found = numbers.find(num => num > 3); // 4
const foundIndex = numbers.findIndex(num => num === 3); // 2
console.log('找到大于3的第一个数:', found, ';3的索引:', foundIndex);

// 5. 归约 (reduce): 对数组中的所有元素执行一个归约函数,将其归约成一个单一的值
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0); // 15
console.log('总和:', sum);

// 6. 展开运算符 (...) 和数组合并
const arr1 = [1, 2];
const arr2 = [3, 4];
const combinedArr = [...arr1, ...arr2]; // [1, 2, 3, 4]
const newArrWithAdditions = [...numbers, 6, 7]; // [1, 2, 3, 4, 5, 6, 7]
console.log('合并数组:', combinedArr);

// 7. 对象属性的遍历 (ES6)
const user = { name: 'Alice', age: 30, city: 'New York' };

for (const key in user) {
    if (Object.hasOwnProperty.call(user, key)) {
        console.log(`${key}: ${user[key]}`);
    }
}

Object.keys(user).forEach(key => {
    console.log(`Key: ${key}, Value: ${user[key]}`);
});

Object.values(user).forEach(value => {
    console.log(`Value: ${value}`);
});

Object.entries(user).forEach(([key, value]) => {
    console.log(`Entry - Key: ${key}, Value: ${value}`);
});

3.3 异步操作:Promise 和 Async/Await

处理网络请求和耗时操作的现代方式,避免回调地狱。

// 1. Promise 基本用法
function fetchData(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.onload = () => {
            if (xhr.status === 200) {
                resolve(JSON.parse(xhr.responseText));
            } else {
                reject(new Error(`请求失败: ${xhr.status}`));
            }
        };
        xhr.onerror = () => {
            reject(new Error('网络错误'));
        };
        xhr.send();
    });
}

fetchData('https://api.example.com/data')
    .then(data => {
        console.log('数据获取成功:', data);
        return fetchData('https://api.example.com/more-data?id=' + data.id); // 链式调用
    })
    .then(moreData => {
        console.log('更多数据获取成功:', moreData);
    })
    .catch(error => {
        console.error('数据获取失败:', error);
    })
    .finally(() => {
        console.log('请求完成,无论成功或失败。');
    });

// 2. Async/Await (基于 Promise 的语法糖)
async function getPosts() {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts/1'); // 等待 fetch 完成
        if (!response.ok) {
            throw new Error(`HTTP 错误! 状态: ${response.status}`);
        }
        const post = await response.json(); // 等待 JSON 解析完成
        console.log('获取到的 Post:', post);

        const commentsResponse = await fetch(`https://jsonplaceholder.typicode.com/posts/${post.id}/comments`);
        if (!commentsResponse.ok) {
            throw new Error(`HTTP 错误! 状态: ${commentsResponse.status}`);
        }
        const comments = await commentsResponse.json();
        console.log('获取到的评论:', comments);

    } catch (error) {
        console.error('获取数据时发生错误:', error);
    } finally {
        console.log('异步操作结束。');
    }
}

getPosts();

// 3. Promise.all() - 并行处理多个 Promise
const promise1 = Promise.resolve(3);
const promise2 = 42; // 非 Promise 值也会被包装成 Promise
const promise3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3])
    .then((values) => {
        console.log('所有 Promise 都已解决:', values); // [3, 42, "foo"]
    })
    .catch(error => {
        console.error('其中一个 Promise 失败:', error);
    });

// 4. Promise.race() - 哪个 Promise 先解决或拒绝,就返回哪个结果
const p1 = new Promise((resolve, reject) => setTimeout(resolve, 500, 'one'));
const p2 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'two'));

Promise.race([p1, p2])
    .then((value) => {
        console.log('Race Winner:', value); // 'two'
    });

3.4 表单验证

客户端表单验证是提升用户体验的重要环节。

// HTML 结构 (假设有一个简单的表单)
/*
<form id="myForm">
    <input type="email" id="emailInput" placeholder="请输入邮箱">
    <div id="emailError" style="color: red;"></div>
    <input type="password" id="passwordInput" placeholder="请输入密码">
    <div id="passwordError" style="color: red;"></div>
    <button type="submit">提交</button>
</form>
*/

document.addEventListener('DOMContentLoaded', () => {
    const myForm = document.getElementById('myForm');
    const emailInput = document.getElementById('emailInput');
    const passwordInput = document.getElementById('passwordInput');
    const emailError = document.getElementById('emailError');
    const passwordError = document.getElementById('passwordError');

    function validateEmail(email) {
        const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return re.test(String(email).toLowerCase());
    }

    function validatePassword(password) {
        // 密码至少6位,包含数字和字母
        return password.length >= 6 && /[0-9]/.test(password) && /[a-zA-Z]/.test(password);
    }

    myForm.addEventListener('submit', function(event) {
        event.preventDefault(); // 阻止表单默认提交行为

        let isValid = true;

        // 验证邮箱
        if (!validateEmail(emailInput.value)) {
            emailError.textContent = '请输入有效的邮箱地址。';
            isValid = false;
        } else {
            emailError.textContent = '';
        }

        // 验证密码
        if (!validatePassword(passwordInput.value)) {
            passwordError.textContent = '密码至少6位,且包含数字和字母。';
            isValid = false;
        } else {
            passwordError.textContent = '';
        }

        if (isValid) {
            alert('表单验证通过,准备提交!');
            // 可以在这里发送 AJAX 请求提交表单数据
            // this.submit(); // 或者如果希望原生提交,取消注释
        } else {
            console.log('表单验证失败!');
        }
    });

    // 实时验证 (可选)
    emailInput.addEventListener('input', () => {
        if (emailInput.value === '' || validateEmail(emailInput.value)) {
            emailError.textContent = '';
        }
    });

    passwordInput.addEventListener('input', () => {
        if (passwordInput.value === '' || validatePassword(passwordInput.value)) {
            passwordError.textContent = '';
        }
    });
});

3.5 localStorage 和 sessionStorage

浏览器本地存储,用于持久化数据或会话数据。

// 设置数据
localStorage.setItem('username', 'Alice');
sessionStorage.setItem('tempData', JSON.stringify({ item1: 'value1', item2: 'value2' }));

// 获取数据
const username = localStorage.getItem('username');
const tempData = JSON.parse(sessionStorage.getItem('tempData'));

console.log('从 localStorage 获取:', username); // Alice
console.log('从 sessionStorage 获取:', tempData); // { item1: 'value1', item2: 'value2' }

// 移除数据
localStorage.removeItem('username');
sessionStorage.clear(); // 清除 sessionStorage 中所有数据

// 检查某个键是否存在
if (localStorage.getItem('username') === null) {
    console.log('username 已被移除。');
}

// 遍历所有存储的键
for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    const value = localStorage.getItem(key);
    console.log(`localStorage Key: ${key}, Value: ${value}`);
}

四、实用工具函数

编写可复用的工具函数是提高代码质量和开发效率的关键。

4.1 防抖 (Debounce)

在事件持续触发时,只在事件停止后 N 毫秒执行一次回调函数。常用于搜索框输入、窗口 resize 等。

/**
 * 防抖函数
 * @param {Function} func - 要执行的函数
 * @param {number} delay - 延迟时间(毫秒)
 * @param {boolean} immediate - 是否立即执行一次
 * @returns {Function} 防抖后的函数
 */
function debounce(func, delay, immediate = false) {
    let timeout;
    let result;

    return function(...args) {
        const context = this; // 保存函数上下文

        const later = function() {
            timeout = null;
            if (!immediate) {
                result = func.apply(context, args);
            }
        };

        const callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, delay);

        if (callNow) {
            result = func.apply(context, args);
        }

        return result;
    };
}

// 示例使用
function search(query) {
    console.log('正在搜索:', query);
}

const debouncedSearch = debounce(search, 500);

// 模拟用户输入
// debouncedSearch('hello');
// debouncedSearch('hello world'); // 0.1秒后再次输入,会重新计时
// setTimeout(() => debouncedSearch('final query'), 600); // 最终只会执行这一次

document.addEventListener('DOMContentLoaded', () => {
    const inputElement = document.getElementById('searchInput');
    if (inputElement) {
        inputElement.addEventListener('input', debounce((e) => {
            console.log('防抖后的输入内容:', e.target.value);
        }, 500));
    }
});

// HTML 假设: <input type="text" id="searchInput" placeholder="搜索...">

4.2 节流 (Throttle)

在事件持续触发时,每隔 N 毫秒执行一次回调函数。常用于滚动加载、拖拽事件等。

/**
 * 节流函数
 * @param {Function} func - 要执行的函数
 * @param {number} interval - 间隔时间(毫秒)
 * @returns {Function} 节流后的函数
 */
function throttle(func, interval) {
    let timeout;
    let lastArgs;
    let lastThis;
    let lastExecTime = 0; // 上次执行时间

    return function(...args) {
        const context = this;
        const now = Date.now();

        // 首次执行或距离上次执行超过间隔时间
        if (now - lastExecTime > interval) {
            func.apply(context, args);
            lastExecTime = now;
        } else {
            // 在间隔时间内,设置定时器,等待时间到了再执行
            lastArgs = args;
            lastThis = context;
            clearTimeout(timeout);
            timeout = setTimeout(() => {
                func.apply(lastThis, lastArgs);
                lastExecTime = Date.now(); // 定时器执行后更新时间
            }, interval - (now - lastExecTime));
        }
    };
}

// 示例使用
function handleScroll() {
    console.log('滚动事件触发...');
}

const throttledScroll = throttle(handleScroll, 1000); // 每秒最多执行一次

// window.addEventListener('scroll', throttledScroll);

4.3 cookie 操作

一个简单的用于设置、获取和删除 Cookie 的工具函数。

// 设置 Cookie
function setCookie(name, value, days) {
    let expires = "";
    if (days) {
        const date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        expires = "; expires=" + date.toUTCString();
    }
    document.cookie = name + "=" + (value || "")  + expires + "; path=/";
}

// 获取 Cookie
function getCookie(name) {
    const nameEQ = name + "=";
    const ca = document.cookie.split(';');
    for(let i=0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) === ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
}

// 删除 Cookie
function eraseCookie(name) {
    document.cookie = name + '=; Max-Age=-99999999; path=/'; // 设置过期时间为过去
}

// 示例
setCookie('user_token', 'abc123xyz', 7); // 设置名为 user_token 的 cookie,7天后过期
console.log('获取 user_token:', getCookie('user_token'));

setCookie('lang', 'zh-CN'); // 会话 Cookie,浏览器关闭即失效
console.log('获取 lang:', getCookie('lang'));

eraseCookie('lang');
console.log('删除 lang 后再获取:', getCookie('lang')); // null

4.4 URL 参数解析

从 URL 中获取查询参数。

/**
 * 解析 URL 查询参数
 * @param {string} url - 待解析的 URL 字符串 (可选,默认为当前页面 URL)
 * @returns {Object} 包含所有查询参数的对象
 */
function getQueryParams(url = window.location.search) {
    const params = {};
    // 确保处理的是查询字符串部分
    const queryString = url.includes('?') ? url.split('?')[1] : url;

    if (queryString) {
        queryString.split('&').forEach(param => {
            const parts = param.split('=');
            const key = decodeURIComponent(parts[0]);
            const value = parts.length > 1 ? decodeURIComponent(parts.slice(1).join('=')) : '';
            params[key] = value;
        });
    }
    return params;
}

// 示例
const currentUrlParams = getQueryParams();
console.log('当前 URL 参数:', currentUrlParams); // 例如: { id: "123", name: "test" }

const customUrlParams = getQueryParams('https://example.com/page?category=webdev&sort=date&filter=hot%20topics');
console.log('自定义 URL 参数:', customUrlParams); // { category: "webdev", sort: "date", filter: "hot topics" }

// 使用 URLSearchParams (现代浏览器推荐)
function getQueryParamsModern(url = window.location.search) {
    const params = {};
    const urlParams = new URLSearchParams(url);
    for (const [key, value] of urlParams.entries()) {
        params[key] = value;
    }
    return params;
}
console.log('现代方法获取 URL 参数:', getQueryParamsModern('https://example.com/page?category=webdev&sort=date'));

五、性能优化相关

前端性能是用户体验的基石,以下是一些常见的优化技巧。

5.1 图片懒加载 (Lazy Load)

只有当图片进入视口时才加载,减少首屏加载时间和带宽消耗。

<!-- HTML 结构 -->
<img data-src="path/to/image1.jpg" alt="Description 1" class="lazyload-img">
<img data-src="path/to/image2.png" alt="Description 2" class="lazyload-img">
<img data-src="path/to/image3.gif" alt="Description 3" class="lazyload-img">

<!-- 可以添加一个占位符图片或者低质量图片 -->
<img src="placeholder.jpg" data-src="path/to/image4.jpg" alt="Description 4" class="lazyload-img">
document.addEventListener('DOMContentLoaded', () => {
    const lazyImages = document.querySelectorAll('.lazyload-img');

    if ('IntersectionObserver' in window) {
        // 使用 Intersection Observer API
        const observer = new IntersectionObserver((entries, observer) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const img = entry.target;
                    img.src = img.dataset.src; // 将 data-src 赋值给 src
                    img.removeAttribute('data-src'); // 移除 data-src 属性
                    img.onload = () => {
                        console.log('图片加载完成:', img.src);
                    };
                    img.onerror = () => {
                        console.error('图片加载失败:', img.src);
                    };
                    observer.unobserve(img); // 一旦加载,停止观察
                }
            });
        }, {
            rootMargin: '0px 0px 100px 0px' // 在图片进入视口前 100px 开始加载
        });

        lazyImages.forEach(img => {
            observer.observe(img);
        });
    } else {
        // 降级方案:监听滚动事件 (效率较低,不推荐用于大量图片)
        console.warn('浏览器不支持 IntersectionObserver,使用降级方案。');
        let lazyLoadThrottleTimeout;

        function lazyload() {
            if (lazyLoadThrottleTimeout) {
                clearTimeout(lazyLoadThrottleTimeout);
            }
            lazyLoadThrottleTimeout = setTimeout(() => {
                lazyImages.forEach(img => {
                    // getBoundingClientRect().top 小于 window.innerHeight (图片顶部进入视口)
                    // 或者 img.getBoundingClientRect().bottom 大于 0 (图片底部仍在视口内)
                    if (img.dataset.src && img.getBoundingClientRect().top < window.innerHeight + 100) {
                        img.src = img.dataset.src;
                        img.removeAttribute('data-src');
                        img.onload = () => console.log('图片加载完成 (降级):', img.src);
                        img.onerror = () => console.error('图片加载失败 (降级):', img.src);
                    }
                });
                // 移除已加载的图片,减少循环次数
                lazyImages = Array.from(lazyImages).filter(img => img.hasAttribute('data-src'));
                if (lazyImages.length === 0) {
                    window.removeEventListener('scroll', lazyload);
                    window.removeEventListener('resize', lazyload);
                }
            }, 200); // 节流
        }

        window.addEventListener('scroll', lazyload);
        window.addEventListener('resize', lazyload);
        lazyload(); // 首次加载执行一次
    }
});

注意: 对于图片懒加载,强烈推荐使用 Intersection Observer API,因为它比传统的滚动监听性能更好、更高效。如果需要兼容老旧浏览器,才考虑滚动监听的降级方案。

5.2 事件委托 (Event Delegation)

将事件监听器添加到父元素而非每个子元素,提高性能,尤其适用于动态生成的元素。

// HTML 假设
/*
<ul id="myList">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>
<button id="addItemBtn">添加新项目</button>
*/

document.addEventListener('DOMContentLoaded', () => {
    const myList = document.getElementById('myList');
    const addItemBtn = document.getElementById('addItemBtn');

    // 使用事件委托
    if (myList) {
        myList.addEventListener('click', function(event) {
            // 检查点击事件是否来源于 li 元素或其内部
            if (event.target.tagName === 'LI') {
                console.log('点击了列表项:', event.target.textContent);
                event.target.style.backgroundColor = 'yellow';
            }
        });
    }

    // 动态添加新项目
    if (addItemBtn) {
        let itemCount = 3;
        addItemBtn.addEventListener('click', () => {
            itemCount++;
            const newItem = document.createElement('li');
            newItem.textContent = `Item ${itemCount} (新)`;
            myList.appendChild(newItem);
            console.log('添加了新项目。');
        });
    }
});
graph TD A[事件委托] --> B{父元素添加监听器}; B --> C[事件冒泡]; C --> D{判断 event.target}; D -->|是目标元素| E[执行回调]; D -->|不是目标元素| F[不执行]; G[普通事件监听] --> H{每个子元素添加监听器}; H -->|子元素过多或动态生成| I[性能开销大]; style A fill:#f9f,stroke:#333,stroke-width:2px; style B fill:#bbf,stroke:#333,stroke-width:2px; style C fill:#bbf,stroke:#333,stroke-width:2px; style D fill:#fbf,stroke:#333,stroke-width:2px; style E fill:#bfb,stroke:#333,stroke-width:2px; style F fill:#fbb,stroke:#333,stroke-width:2px; style G fill:#ff9,stroke:#333,stroke-width:2px; style H fill:#fba,stroke:#333,stroke-width:2px; style I fill:#f55,stroke:#333,stroke-width:2px;

六、常用正则表达式

用于字符串匹配和验证。

// 1. 验证邮箱
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log('[email protected] 邮箱验证:', emailRegex.test('[email protected]')); // true
console.log('invalid-email 邮箱验证:', emailRegex.test('invalid-email')); // false

// 2. 验证手机号 (中国大陆,宽松匹配)
const phoneRegex = /^1[3-9]\d{9}$/;
console.log('13812345678 手机号验证:', phoneRegex.test('13812345678')); // true
console.log('12345678901 手机号验证:', phoneRegex.test('12345678901')); // false

// 3. 验证 URL
const urlRegex = /^(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/[a-zA-Z0-9]+\.[^\s]{2,}|[a-zA-Z0-9]+\.[^\s]{2,})$/i;
console.log('https://www.google.com URL 验证:', urlRegex.test('https://www.google.com')); // true
console.log('invalid url URL 验证:', urlRegex.test('invalid url')); // false

// 4. 验证数字 (整数或浮点数)
const numberRegex = /^-?\d+(\.\d+)?$/;
console.log('123 数字验证:', numberRegex.test('123')); // true
console.log('3.14 数字验证:', numberRegex.test('3.14')); // true
console.log('-100 数字验证:', numberRegex.test('-100')); // true
console.log('abc 数字验证:', numberRegex.test('abc')); // false

// 5. 替换字符串中的所有空格
const textWithSpaces = "Hello   World   !";
const noSpacesText = textWithSpaces.replace(/\s+/g, ''); // g 表示全局匹配,+ 表示匹配一个或多个空格
console.log('移除空格:', noSpacesText); // HelloWorld!

// 6. 提取数字
const strWithNumbers = "订单号: ORD12345, 金额: 99.50元";
const numbersExtracted = strWithNumbers.match(/\d+(\.\d+)?/g);
console.log('提取数字:', numbersExtracted); // ["12345", "99.50"]

七、其他常用技巧

7.1 深拷贝对象 (Deep Clone)

避免修改引用类型数据时影响原始数据。

// 简单深拷贝 (适用于 JSON 安全的数据类型:数字、字符串、布尔、null、数组、普通对象)
function deepCloneSimple(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    return JSON.parse(JSON.stringify(obj));
}

const originalObjSimple = {
    name: 'Test',
    age: 25,
    address: { city: 'Beijing', street: 'Road A' },
    hobbies: ['coding', 'reading'],
    func: function() { console.log('hello'); }, // 函数会被忽略
    date: new Date() // Date 对象会变成字符串
};
const clonedObjSimple = deepCloneSimple(originalObjSimple);
clonedObjSimple.address.city = 'Shanghai';
clonedObjSimple.hobbies.push('travel');

console.log('原始对象 (简单):', originalObjSimple);
console.log('克隆对象 (简单):', clonedObjSimple);

// 更完善的深拷贝 (处理函数、Date、RegExp、循环引用等,更复杂,通常会用 lodash.cloneDeep)
function deepClone(obj, hash = new WeakMap()) {
    if (obj === null || typeof obj !== 'object') return obj;

    // 避免循环引用
    if (hash.has(obj)) return hash.get(obj);

    // 处理特殊对象类型
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);

    const cloneObj = Array.isArray(obj) ? [] : {};
    hash.set(obj, cloneObj); // 存储已克隆的对象

    for (const key in obj) {
        // 确保是对象自身的属性,而不是原型链上的属性
        if (Object.hasOwnProperty.call(obj, key)) {
            cloneObj[key] = deepClone(obj[key], hash);
        }
    }
    return cloneObj;
}

const originalObjComplex = {
    a: 1,
    b: { c: 2 },
    d: [3, { e: 4 }],
    f: new Date(),
    g: function() { console.log('complex'); },
    h: /test/g
};
originalObjComplex.self = originalObjComplex; // 循环引用

const clonedObjComplex = deepClone(originalObjComplex);
clonedObjComplex.b.c = 99;
clonedObjComplex.d[1].e = 88;
clonedObjComplex.g(); // 可以执行

console.log('原始对象 (复杂):', originalObjComplex);
console.log('克隆对象 (复杂):', clonedObjComplex);
console.log('循环引用是否正确处理:', clonedObjComplex.self === clonedObjComplex);

7.2 国际化 (i18n) 基础

根据用户语言显示不同文本。

const messages = {
    'en': {
        'greeting': 'Hello, {name}!',
        'farewell': 'Goodbye!',
        'welcome': 'Welcome to our website!'
    },
    'zh-CN': {
        'greeting': '你好,{name}!',
        'farewell': '再见!',
        'welcome': '欢迎来到我们的网站!'
    },
    'es': {
        'greeting': '¡Hola, {name}!',
        'farewell': '¡Adiós!',
        'welcome': '¡Bienvenido a nuestro sitio web!'
    }
};

/**
 * 获取翻译文本
 * @param {string} key - 翻译键
 * @param {string} [locale=navigator.language] - 语言环境,默认为浏览器语言
 * @param {Object} [params={}] - 替换参数,例如 { name: 'Alice' }
 * @returns {string} 翻译后的文本
 */
function getTranslation(key, locale = navigator.language, params = {}) {
    // 简单处理语言代码,例如 zh-CN -> zh
    const primaryLocale = locale.split('-')[0];

    let translation = messages[locale]?.[key] || messages[primaryLocale]?.[key] || messages['en'][key];

    if (!translation) {
        return `[Missing translation for ${key}]`;
    }

    // 替换参数
    for (const paramKey in params) {
        if (Object.hasOwnProperty.call(params, paramKey)) {
            translation = translation.replace(`{${paramKey}}`, params[paramKey]);
        }
    }

    return translation;
}

// 示例使用
console.log(getTranslation('welcome', 'zh-CN')); // 欢迎来到我们的网站!
console.log(getTranslation('greeting', 'en', { name: 'Bob' })); // Hello, Bob!
console.log(getTranslation('farewell', 'es')); // ¡Adiós!
console.log(getTranslation('unknown_key', 'zh-CN')); // [Missing translation for unknown_key]

// 动态设置语言
let currentLanguage = 'en';
function setLanguage(lang) {
    currentLanguage = lang;
    document.documentElement.lang = lang; // 设置 HTML lang 属性
    console.log(`语言已切换到: ${lang}`);
    // 重新渲染页面或更新相关文本
}

// setLanguage('zh-CN');
// console.log(getTranslation('greeting', currentLanguage, { name: '李华' }));

7.3 自定义事件

在组件之间或非父子关系之间进行通信。

document.addEventListener('DOMContentLoaded', () => {
    const eventLog = document.getElementById('eventLog');
    if (!eventLog) return;

    function logEvent(message) {
        const li = document.createElement('li');
        li.textContent = message;
        eventLog.appendChild(li);
    }

    // 1. 创建自定义事件
    const myCustomEvent = new CustomEvent('dataLoaded', {
        detail: {
            // detail 属性用于传递额外数据
            timestamp: Date.now(),
            data: { id: 1, name: 'Sample Data' }
        },
        bubbles: true,   // 事件是否冒泡
        cancelable: true // 事件是否可以取消 (preventDefault())
    });

    // 2. 监听自定义事件
    document.addEventListener('dataLoaded', function(event) {
        logEvent(`'dataLoaded' 事件触发!`);
        logEvent(`事件源: ${event.target.id || 'document'}`);
        logEvent(`详细数据: ${JSON.stringify(event.detail, null, 2)}`);

        if (event.cancelable && Math.random() < 0.5) {
            event.preventDefault(); // 模拟取消事件
            logEvent('事件被取消!');
        }
    });

    // 3. 触发自定义事件
    setTimeout(() => {
        logEvent('正在派发 dataLoaded 事件...');
        document.dispatchEvent(myCustomEvent); // 在 document 上派发
    }, 1000);

    // 另一个在特定元素上触发的例子
    const customElement = document.createElement('div');
    customElement.id = 'my-custom-element';
    customElement.style.cssText = 'padding: 10px; background: #e0f7fa; border: 1px solid #b2ebf2; margin-top: 20px;';
    customElement.textContent = '点击我派发事件';
    document.body.appendChild(customElement);

    customElement.addEventListener('click', () => {
        const anotherCustomEvent = new CustomEvent('elementClicked', {
            detail: { clicks: 1 },
            bubbles: true
        });
        customElement.dispatchEvent(anotherCustomEvent);
        logEvent('Element Clicked 事件在 customElement 上派发。');
    });

    document.addEventListener('elementClicked', (event) => {
        logEvent(`'elementClicked' 事件捕获 (冒泡):`);
        logEvent(`  事件源: ${event.target.id}`);
        logEvent(`  点击次数: ${event.detail.clicks}`);
    });
});

/* HTML 结构补充:
<ul id="eventLog" style="border: 1px solid #ddd; padding: 10px; min-height: 100px; background: #f9f9f9;">
    <li>事件日志:</li>
</ul>
*/

总结: 以上就是前端开发中一些非常常见且实用的代码片段。它们涵盖了从页面的结构、样式到交互逻辑的方方面面。掌握并灵活运用这些片段,将大大提高您的开发效率,帮助您构建更加健壮、高效和用户友好的前端应用。

作为一名前端架构师,我建议您不仅仅是复制粘贴这些代码,更重要的是理解它们背后的原理和最佳实践。持续学习和实践,是成为一名优秀前端工程师的关键。

互动区域

登录后可以点赞此内容

参与互动

登录后可以点赞和评论此内容,与作者互动交流