A collection of hands-on web exercises and experiments. Like a latte—one part espresso, two parts milk, and one part foam—a familiar, approachable blend for everyday learning.
一组动手实践的 Web 练习与实验。就像一杯拿铁——一份浓缩、两份牛奶、一份奶泡——熟悉、易入口,适合日常学习。
Learn how the web works by building small versions of it: Promise/A+, mini React, a router, a bundler and more. Run a page, follow the guide beside it, then read the code.
74 examples · 872/872 Promises/A+ tests · every page tested in Chromium, Firefox and WebKit
How JavaScript and the browser behave, one small page at a time.
JavaScript 基础:闭包、this 与 new
Short scripts that show how closures, this and object construction really work.
手写 call、apply 与 bind
Rebuild call, apply and bind to see how a function decides what this is.
手写 new 与 instanceof
Rebuild new and instanceof: create the object, run the constructor, walk the prototype chain.
class extends 背后做了什么
Compare class extends with old-style function inheritance, including why static members are inherited too.
Promise 链与 async/await 对照
The same two-step calculation written with .then() and with await.
事件传播与事件循环
Watch events bubble, then predict and check the order of tasks, microtasks and timers.
移动元素:改布局还是用 transform
Move the same box with top and with transform, and see which one makes the browser redo layout.
CSS 布局:BFC、Grid 与居中
Block formatting contexts, grid placement and several ways to center an element.
语义化 HTML
Build a page from meaningful sections instead of anonymous boxes.
TypeScript:给 action 和 reducer 加类型
A discriminated union tells the compiler which action each branch of a reducer handles.
Read · 阅读README
Small, tested versions of tools you use every day.
从零实现 Promise
Grow a Promise in three steps, from a tiny state machine to a version that passes all 872 official Promises/A+ tests.
Generator 如何暂停与恢复
See the state machine that Babel turns yield into, and how values flow in and out.
小例子讲设计模式
Singleton, factory, adapter, proxy and publish/subscribe, each in a few lines.
原型继承,以及常见的错误写法
See what goes wrong with a shared prototype or a borrowed constructor, and how Object.create fixes it.
Proxy 拦截与事件委托
Intercept property access with Proxy, and handle many clicks from one parent element.
发布订阅
A minimal on / emit / off event hub; senders never need to know who is listening.
Read · 阅读README
手写常用工具函数
Debounce, throttle, deep clone and more, written as short algorithms with clear limits.
Read · 阅读README
浅拷贝与深拷贝
Copy an object graph while keeping shared references and cycles, then compare with structuredClone.
检测循环引用
Tell a real cycle apart from an object that is referenced twice.
防抖
Wait until a burst of calls stops, then run once.
节流
Run at most once per interval; the first call goes through right away.
定时器为什么会漂移,以及如何校正
Measure how late timer callbacks arrive and adjust the next deadline.
千分位格式化,不丢精度
Group the digits of a number string without converting it to Number; compare with Intl.NumberFormat.
add(1)(2)(3):柯里化与类型转换
Keep a running total in a closure, and see how a function turns into a number.
手写打包器(两种写法)
Parse imports, build a dependency graph and emit one file: first as plain functions, then as a small compiler.
手写一个 React 风格的渲染器
createElement, mount and setState in a few files; two counters keep their own state.
手写前端路由
A small router on the History API: links, back and forward, all without a page reload.
函数组合(compose)
The compose helper behind Redux middleware, written by hand.
可编辑文本里的光标与选区
Save the cursor before focus leaves, insert text at it, and restore it after re-rendering.
How source files become something a browser can load.
webpack 逐项拆解
A set of small builds, each changing one thing: entries, loaders, source maps, hot reload, splitting, caching and more.
Read · 阅读README
第一个 webpack 构建
One entry file becomes one bundle; follow the dependency graph in between.
加载 CSS、图片和数据文件
Import CSS, an SVG and an XML file, and see which loader or asset type handles each one.
多个入口
Two entries produce two named files, and the HTML plugin adds their script tags for you.
用 source map 调试
Build in development mode and read your original source in DevTools.
模块热替换(HMR)
Edit a module while the dev server runs, and the page updates without a reload.
用 import() 按需加载
A module is downloaded only when you click the button.
多入口共享代码
Two entries share one copy of Lodash instead of bundling it twice.
用内容哈希做长期缓存
File names change only when their content changes, so browsers can cache them safely.
开发构建与生产构建
One project, two configs: readable output for debugging, optimized output for users.
Tree shaking:去掉没用到的导出
Import one function and watch the unused one disappear from the production bundle.
为旧式全局代码做适配(shimming)
Give legacy code the global it expects, even though it never imports it.
编写 webpack 插件
A plugin that hooks into the build and writes a list of every output file.
用 webpack 打包一个库
Package a tiny number-to-word converter as a library and load it from a separate Node program.
Read · 阅读README
Grunt:基于任务的构建
A clean-then-copy build, the way many projects worked before bundlers.
Less,以及今天原生 CSS 能做到的
Variables, mixins and guards in Less, and why native CSS variables can change at runtime while Less variables cannot.
模板编译与 HTML 转义
Compile a Handlebars template and see why {{value}} is escaped while {{{value}}} is not.
Gulp:按顺序执行任务
Clean, compile TypeScript and copy HTML as a small Gulp pipeline.
通过 webpack 使用 TypeScript 和 React
See why Babel strips types without checking them, and where tsc still fits in.
Complete applications with old and new versions side by side, plus visual experiments.
一个组件库,三种构建方式
Ship the same Card and Button with Vite, webpack and Babel, and load each output.
2018 年风格的 Todo:class、Redux、Saga、Immutable
The original architecture, repaired and running on React 19.
今天风格的 Todo:Hooks、TypeScript、Redux Toolkit
The same features as the 2018 version, so the two can be compared side by side.
用 Draft.js 做富文本(已归档)
Type, bold and save as JSON with the editor Meta archived in 2023.
用 Lexical 做富文本
The same type, bold and save steps with Draft.js's successor.
JSONP 与 fetch + CORS 对照
Read the same cross-origin data two ways, and cancel a request with AbortController.
Runs locally · 需本地运行README
用 Service Worker 实现离线访问
Register, cache, serve offline and clean up, without a framework.
从零搭一个 GraphQL HTTP 服务
Schema, resolvers and a JSON response: the smallest complete GraphQL request.
Runs locally · 需本地运行README
全栈 GraphQL:Apollo、订阅与 SQLite
A small link feed with sign-up, voting, pagination and live updates.
Runs locally · 需本地运行README
GraphQL 服务端与数据库
Apollo Server, Prisma and SQLite, rebuilt from migrations and a fictional seed.
Read · 阅读README
Apollo Client 前端
Queries, mutations and live subscriptions from a Vite app.
Read · 阅读README
在浏览器里运行 Rust(WebAssembly)
Compile a Rust function to .wasm and call it from a button.
Canvas 点阵倒计时
Digits drawn from a grid of dots; when a digit changes, its dots fall away as particles.
用 Canvas 像素做图像处理
Scale, watermark, magnify and filter an image by reading and writing its pixels.
用鼠标事件和 Pointer Events 实现拖拽
The same drag written twice; Pointer Events also cover touch and pen.
滑动翻页
Turn a swipe into a page change using distance and speed thresholds, with touch events and with Pointer Events.
3D 轮播与 CSS Scroll Snap
Place layered slides with geometry, then let CSS Scroll Snap do similar work natively.
CSS transform 照片墙
Scattered, tilted cards that straighten and zoom in on hover.
可以用键盘操作的搜索建议
Filter suggestions as you type and choose one with the arrow keys, using accessible combobox markup.
导航、步骤条与环形进度
An expanding menu (JavaScript tween vs CSS transition), a step bar and a conic-gradient progress ring.
抽奖转盘
Spin a wheel that always stops on the sector it reports, using the Web Animations API.
视觉实验
Canvas, CSS and pointer experiments that run straight in the browser.
Read · 阅读README
今天怎样测页面速度
Navigation Timing, PerformanceObserver and Core Web Vitals, measured on your own visit.
Notes kept from earlier years. Read them for context; they are not current advice.
2018 年的 React 架构笔记
The original notes (in Chinese) on domain-driven structure, sagas and routing.
Read · 阅读README
老式 React 测试:TestUtils 与 Enzyme
Shallow rendering and DOM tests from an older tutorial, kept for comparison with Testing Library.
Read · 阅读README
2019 年怎样测页面速度
How page speed was measured with performance.timing, and why those numbers are read differently now.
Read · 阅读README