← 返回 代码:从基础到能独立开发
这份东西怎么用 不是让你从第一行读到最后一行。做法是:想到一个要做的东西 → 从下面找你需要的那一格 → 复制代码 → 改成你要的 → 卡住了再回来查。知识点只有被用过才记得住,读过的不算。
你已经会基础,所以真正的风险不是「不会」,是「只学不做」 你能看懂教程但写不出东西,是因为一直在「输入」。从今天起规则改成:48 小时内必须有一个能双击打开的东西。丑、小、没用都行,但它得存在。

A · 地基:HTML + CSS

1. HTML 骨架与语义标签

干什么:决定页面上「有什么」。语义标签的作用是让代码可读、让手机和搜索引擎看得懂。

<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>标题</title>
</head>
<body>
  <header>页头</header>
  <nav>导航</nav>
  <main>
    <section>
      <h1>一级标题</h1>
      <p>段落</p>
    </section>
  </main>
  <footer>页脚</footer>
</body>
</html>

记住三条:meta viewport 没写,手机上就是缩小版网页;一个页面只有一个 h1;img 一定要有 alt。

2. CSS 选择器与盒模型

干什么:决定「长什么样」。盒模型是所有布局的地基:每个元素都是一个盒子,由内容 + 内边距 + 边框 + 外边距组成。

/* 类选择器最常用,id 少用 */
.card { }
.card .title { }
.card:hover { }

/* 让宽高含 padding 和 border —— 必写这行 */
* { box-sizing: border-box; }

.card {
  width: 300px;
  padding: 16px;         /* 内边距 */
  border: 1px solid #333;
  margin: 12px auto;     /* 上下 12,左右居中 */
}

优先级:行内样式 > id > 类 > 标签。写不动的样式大多是优先级被压住了,先查这个再用 !important。

3. Flex 布局(90% 的横向排列都用它)

干什么:一行里怎么摆、怎么对齐、怎么分配空间。

.row {
  display: flex;
  justify-content: space-between; /* 主轴:靠边分 */
  align-items: center;           /* 交叉轴:垂直居中 */
  gap: 12px;                     /* 间距,比 margin 干净 */
  flex-wrap: wrap;               /* 放不下就换行 */
}
.row .grow { flex: 1; }          /* 这个子元素吃掉剩余空间 */

口诀:justify 管主轴,align 管交叉轴。默认横向,加 flex-direction: column 就变纵向,两个轴的作用会互换。

4. Grid 布局(二维格子,做卡片墙和整页排版)
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 12px;
}

auto-fill + minmax 这一行能让卡片自动适配手机和电脑,不用写任何媒体查询。记住它,省很多事。

5. 响应式:媒体查询与相对单位
/* 小于 640px 时生效 */
@media (max-width: 640px) {
  .row { flex-direction: column; }
}

/* 手机安全区(有刘海/小黑条的手机) */
body { padding-bottom: env(safe-area-inset-bottom); }

单位选择:字号用 rem 或 px,宽度用 % 或 fr,间距用 px 就行。别到处 vw,字号会随窗口乱缩。

6. CSS 变量(改一处,全站变)
:root {
  --bg: #0b0f14;
  --txt: #e6edf5;
  --accent: #4ade80;
}
body { background: var(--bg); color: var(--txt); }
.btn { background: var(--accent); }

做主题色、做暗色模式都靠它。写第二次的颜色值就该变成变量,这是判断你有没有在写「能维护的代码」的第一条标准。

7. 过渡与动画
.btn {
  transition: transform .15s, background .2s;
}
.btn:active { transform: scale(.96); }

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.card { animation: fadeUp .3s ease both; }

只动 transform 和 opacity,这两个由显卡处理,不卡。动 width/top/margin 会让浏览器重排,手机上会掉帧。

8. position 定位(弹窗、吸顶、角标)
.bar { position: sticky; top: 0; }   /* 吸顶:滚到顶部就粘住 */

.modal {
  position: fixed;
  inset: 0;                /* 上下左右全是 0 = 铺满 */
  background: rgba(0,0,0,.6);
}

.badge { position: absolute; top: 4px; right: 4px; }

相对定位给父元素 position: relative,绝对定位的子元素才会以父元素为基准。

B · JavaScript:让它动起来

9. 变量、类型、模板字符串
const name = '小明';        // 不会重新赋值,默认用它
let count = 0;              // 会变才用 let
var old = 1;                // 别用

const text = `你好 ${name},你有 ${count} 条消息`;
const list = [1, 2, 3];
const obj = { a: 1, b: 2 };

规矩:默认 const,需要改再换 let。这一个小习惯能消掉你以后一半的低级 bug。

10. 函数与箭头函数
function add(a, b) { return a + b; }

const add2 = (a, b) => a + b;       // 单行可省 return

const user = {
  name: 'A',
  say() { return this.name; }        // 方法用简写
};

箭头函数没有自己的 this,所以对象方法和需要 this 的地方别用箭头函数。

11. 数组四件套:map / filter / reduce / find

这四个能解决 80% 的数据处理。看到 for 循环先想想能不能换成它们。

const nums = [1, 2, 3, 4, 5];

nums.map(n => n * 2);           // [2,4,6,8,10]  逐个转换
nums.filter(n => n % 2 === 0);  // [2,4]         筛出满足的
nums.find(n => n > 3);          // 4             找第一个
nums.reduce((s, n) => s + n, 0); // 15           汇总成一个值

// 实战:把购物车算总价
const cart = [{p:10, n:2}, {p:5, n:3}];
const total = cart.reduce((s, i) => s + i.p * i.n, 0);
12. 对象、解构、展开
const user = { name: 'A', age: 20, city: '北京' };

const { name, age } = user;              // 解构取值
const { city: cityName } = user;         // 改名

const copy = { ...user, age: 21 };       // 复制并改一个字段
const merged = { ...user, ...extra };    // 合并

const [first, ...rest] = [1, 2, 3];      // 数组解构

{...obj, 字段: 新值} 是最常用的「不可变更新」写法,React 里天天用。先把它练成肌肉记忆。

13. 找元素与改元素(DOM)
const el = document.querySelector('.card');
const all = document.querySelectorAll('.card');  // 类数组

el.textContent = '新文字';        // 只写文字
el.innerHTML = '<b>加粗</b>'; // 会解析标签,小心 XSS
el.classList.add('on');
el.classList.toggle('on');
el.style.display = 'none';
el.setAttribute('data-id', '7');

原则:能改 class 就别直接改 style,样式留在 CSS 里。用户输入的内容永远用 textContent,不要 innerHTML。

14. 事件与事件委托
btn.addEventListener('click', (e) => {
  e.preventDefault();          // 阻止默认行为(表单提交/跳转)
  console.log('点了');
});

// 事件委托:给父元素挂一次,管所有子元素
list.addEventListener('click', (e) => {
  const item = e.target.closest('[data-id]');
  if (!item) return;
  handle(item.dataset.id);
});

动态生成的元素用事件委托,否则每加一个元素就要挂一次监听,页面一多就卡。

15. 表单与简单校验
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
  e.preventDefault();
  const data = new FormData(form);
  const name = String(data.get('name') || '').trim();
  if (!name) { alert('名字不能空'); return; }
  save({ name });
});

FormData 一次拿全部字段,比一个个 querySelector 干净得多。

16. localStorage:把数据留在浏览器里
// 存字符串
localStorage.setItem('key', JSON.stringify(data));

// 取,给默认值防止第一次是 null
const data = JSON.parse(localStorage.getItem('key') || '{}');

// 删
localStorage.removeItem('key');

只能存字符串,所以对象要 JSON.stringify。容量约 5MB,清缓存就没了——重要的数据要做「导出」按钮。

17. fetch 请求数据(含报错处理)
async function load() {
  try {
    const res = await fetch('https://api.example.com/list');
    if (!res.ok) throw new Error('HTTP ' + res.status);
    const data = await res.json();
    render(data);
  } catch (err) {
    console.error(err);
    showError('加载失败,请稍后重试');
  }
}

必写 if (!res.ok)。fetch 只在网络断的时候 reject,404/500 它也算成功,不检查你会拿到一堆错误数据还以为没事。

18. 模块化:import / export
// math.js
export function add(a, b) { return a + b; }
export const PI = 3.14;

// main.js
import { add, PI } from './math.js';
import * as math from './math.js';

什么时候拆文件:一个文件超过 300 行,或者某段代码被复制了第二遍。别提前拆。

C · 工程能力:从「会写」到「能交付」

19. Git 只需要先会这 5 条
git init                    # 初始化
git add .                   # 加入暂存
git commit -m "改了什么"     # 提交(一定要写清改了什么)
git log --oneline           # 看历史
git checkout -b 分支名      # 开分支

提交信息写「改了什么」不是「更新」。三个月后你会感谢自己。

20. npm 与 package.json
npm init -y            # 生成 package.json
npm install 包名        # 装到 dependencies
npm install -D 包名     # 装到 devDependencies(只在开发时用)
npm run 脚本名          # 跑 scripts 里的命令

node_modules 永远不要提交、不要复制给别人,别人 npm install 就能还原。

21. Vite:开发环境(热更新 + 打包)
npm create vite@latest my-app
cd my-app
npm install
npm run dev      # 本地开发,改代码浏览器立刻更新
npm run build    # 产出 dist 文件夹,这才是要上线的

先用 Vite,不要一上来学 Webpack。Vite 快、配置少,2026 年的默认选择。

22. 框架怎么选(别纠结,看你要做什么)
要做的用什么理由
一个页面、几个按钮原生 JS引入框架纯属给自己加难度
多个页面、有登录和数据Vue 或 React组件化能撑住复杂度
想尽快找工作React岗位多
想上手快、中文资料多Vue文档友好

核心概念只有一个:数据变了,界面自动跟着变。你不再手动 querySelector 改文字,而是声明「界面长这样,数据是这些」。

23. 组件与状态(框架的骨架)
// Vue 3
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
</script>

<template>
  <button @click="count++">{{ count }} / {{ double }}</button>
</template>
// React
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;

状态就是「会变的数据」。能算出来的不要存成状态(用 computed / 直接算),否则两个值早晚不同步。

24. 路由(多页面应用)
// Vue Router
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
];

单页应用的路由是用 JS 换内容,不刷新页面。所以服务器要做 fallback:所有路径都返回 index.html,否则用户刷新子页面会 404。

25. 最小后端:Node + Express
import express from 'express';
const app = express();
app.use(express.json());

app.get('/api/todos', (req, res) => {
  res.json([{ id: 1, text: '买菜' }]);
});

app.post('/api/todos', (req, res) => {
  const { text } = req.body;
  if (!text) return res.status(400).json({ error: 'text 必填' });
  res.status(201).json({ id: Date.now(), text });
});

app.listen(3000, () => console.log('http://localhost:3000'));

REST 就是「用 HTTP 动词表达动作」:GET 查、POST 建、PUT 改、DELETE 删。路径用名词,别用 /getUser。

26. 数据库:先会 SQL 这 5 条
-- 建表
CREATE TABLE todo (id INTEGER PRIMARY KEY, text TEXT, done INTEGER DEFAULT 0);

SELECT * FROM todo WHERE done = 0 ORDER BY id DESC;
INSERT INTO todo (text) VALUES ('买菜');
UPDATE todo SET done = 1 WHERE id = 3;
DELETE FROM todo WHERE id = 3;

永远用参数化查询(? 占位),不要拼字符串,否则就是 SQL 注入。

27. 部署:两条路
类型放哪要点
纯前端(HTML/CSS/JS)Cloudflare Pages / GitHub Pages / Vercel免费,把这几个文件拖上去就行
有后端Railway / Render / 自己的服务器要配环境变量,数据库单独托管

上线后必做的三件事:换 https、手机上真机打开一次、把报错日志能看到(不然出问题你只能猜)。

D · 怎么算「能独立开发」

验收标准(不是学完多少课,是能不能做到这四件)
  1. 给一个需求,你能拆成页面 + 数据 + 交互三部分,并说出先后顺序。
  2. 不查教程能写出一个带表单、存数据、能刷新的页面。
  3. 报错能自己定位:会看堆栈、会在关键位置打 log、会缩小范围。
  4. 能把东西部署到网上并发给别人一个链接。
这四条都做到,就是能独立开发。跟你背了多少 API 没关系。

新手最常踩的 8 个坑

现象原因解法
样式怎么都不生效选择器优先级被压住先查优先级,别急着 !important
点按钮没反应脚本在元素之前执行script 放 body 末尾或加 defer
数据刷新就没了只存在内存里存 localStorage 或发到后端
fetch 拿到 undefined忘了 await res.json()加 await,并检查 res.ok
循环里改数组越改越乱边遍历边删用 filter 生成新数组
手机上排版崩了写死宽度用 %/fr,加 flex-wrap
上线后 404路径写成了绝对路径用相对路径 ./
改了半天没变化浏览器缓存强刷 Ctrl+F5,或清 Service Worker

第一个能「独立开发」的项目该做多大

一个待办清单,但要真的能用。包含:添加、删除、勾选完成、刷新后数据还在、能按「全部/未完成」筛选、手机上能看。这六条做到,你就跨过了「只会跟着教程敲」那条线。做完立刻部署,拿到那个链接。


← 回今日任务 ← 英语 绘画方法 →