前端科学计算:从原理到边界
一份关于”纯 HTML 文件能否做科学计算”的完整知识梳理。 来源:与 AI 的对话记录,整理为系统化文档。
一、核心结论
纯网页前端完全可以做科学计算。 一个 HTML 文件 + 浏览器,不安装任何软件、不配置任何环境,就能实现表达式求值、矩阵运算、方程求解、微积分、函数绘图、统计分析等功能。将文件夹复制到任意电脑,浏览器打开即可复现。
唯一需要的是引入一个第三方库 math.js 来提供数学能力,获取方式有两种(见下文)。
二、JavaScript 包的加载机制:CDN
2.1 与 Python 的本质区别
Python 的包管理是”下载到本地安装”,JavaScript 在浏览器环境中是”每次从网上取”。
Python 模式——把书买回家:
pip install mathjs
→ 包下载到你电脑硬盘的 site-packages/ 目录
→ 以后每次运行从硬盘读取
→ 断网也能用
JavaScript(浏览器)模式——每次去图书馆借:
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/12.4.0/math.min.js"></script>
→ 浏览器打开网页时,向这个网址发起请求,把代码下载到内存
→ 代码在当前网页的内存中"活"着
→ 关掉网页,内存释放,什么痕迹都不留
→ 断网则无法加载
2.2 CDN 是什么
CDN(Content Delivery Network,内容分发网络)本质上是全球分布的”代码图书馆”。
常见的 CDN 服务:
- cdnjs.cloudflare.com
- unpkg.com
- jsdelivr.net
别人把 math.js 上传到这些平台,全世界任何人都可以通过网址引用。浏览器读到 <script src="网址"> 时,会自动发起 HTTP 请求下载代码,无需人工干预。
2.3 浏览器加载 <script src="..."> 的完整过程
1. 浏览器读取本地硬盘上的 .html 文件
2. 解析 HTML,遇到 <script src="https://..."> 这一行
3. 浏览器向该 URL 发起网络请求(GET)
4. 服务器返回 math.js 的源代码(纯文本)
5. 浏览器将这段代码编译并加载到当前页面的 JavaScript 执行环境中
6. 之后在 <script> 或 JS 代码中写 math.evaluate(...) 就能直接调用
整个过程:
- 没有写入硬盘(浏览器缓存除外)
- 没有修改系统任何设置
- 没有注册表、没有环境变量
- 关掉网页,内存释放
2.4 直观对比表
| 维度 | Python | JavaScript(浏览器) |
|---|---|---|
| 包存储位置 | 本地硬盘 site-packages/ |
互联网 CDN 服务器 |
| 获取方式 | pip install(下载到本地) |
<script src="网址">(运行时从网上取) |
| 断网可用 | 能 | 不能(CDN 方式) |
| 需要安装步骤 | 需要 | 不需要,浏览器自动处理 |
| 版本冲突 | 经常冲突 | 不会,每个网页独立执行环境 |
| “环境配置” | virtualenv、conda 等 | 不存在这个概念 |
三、离线方案:手动下载
如果需要完全离线使用,只需把代码文件保存到本地,改一行路径。
3.1 操作步骤
第一步:在联网状态下,浏览器直接打开 CDN 地址
https://cdnjs.cloudflare.com/ajax/libs/mathjs/12.4.0/math.min.js
Ctrl+S 另存为,放到和 HTML 文件同一个文件夹
第二步:HTML 中的引用从 CDN 改为本地路径
<!-- 从网上借(需联网) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/12.4.0/math.min.js"></script>
<!-- 改成用本地的(完全离线) -->
<script src="math.min.js"></script>
第三步:完事
也可以用命令行下载:
curl -o math.min.js https://cdnjs.cloudflare.com/ajax/libs/mathjs/12.4.0/math.min.js
3.2 最终文件结构
你的文件夹/
├── SciCalc.html ← 主页面(包含所有界面和逻辑)
└── math.min.js ← 数学库(约 500KB)
复制这个文件夹到任何电脑,不管有没有网,双击 HTML 就能用。
四、npm install 与手动下载的区别
虽然两者都是”把东西弄到本地”,但做的事情差距很大。
4.1 手动下载
操作:打开网址 → 右键另存为 → 得到 1 个文件
结果:math.min.js(1 个文件,约 500KB)
浏览器能直接用:<script src="math.min.js">
不需要 Node.js 环境
4.2 npm install
操作:命令行执行 npm install mathjs
结果:自动创建 node_modules/ 目录,内含上百个文件
浏览器能直接用:不能
需要 Node.js 环境
npm install 背后做的事情:
- 下载 mathjs 包(不是 1 个文件,是散装的模块目录结构)
- 创建
node_modules/文件夹 - 自动检查并下载 mathjs 依赖的其他包
- 修改
package.json,记录”装了 mathjs 12.4.0” - 修改
package-lock.json,锁定每个依赖的精确版本
npm install 后的文件结构:
你的文件夹/
├── SciCalc.html
├── package.json
├── package-lock.json
└── node_modules/
├── mathjs/
│ ├── lib/
│ │ ├── core/
│ │ │ ├── function/
│ │ │ │ ├── arithmetic/
│ │ │ │ │ ├── add.js
│ │ │ │ │ ├── subtract.js
│ │ │ │ │ └── ... 几十个文件
│ │ │ │ ├── matrix/
│ │ │ │ ├── trigonometry/
│ │ │ │ └── ...
│ │ └── type/
│ └── package.json
└── 其他被依赖的包...
4.3 为什么浏览器不认识 npm 装的东西
npm 装好的是 Node.js 格式的散装模块,用 require() 或 import 互相引用。浏览器直接打开 HTML 时,不认识 node_modules 目录结构。
要让浏览器用 npm 装的包,需要额外的”构建工具”(Vite、Webpack)把散装文件打包成浏览器认识的格式——这对于”复制文件夹就能用”的需求来说,多此一举。
4.4 npm 的正确使用场景
npm 适合正规的前端工程项目(多人协作、几十个依赖、需要版本管理)。流程是:
npm 进货原材料(node_modules/)
↓
构建工具加工(Vite / Webpack 打包)
↓
产出成品(1 个或几个 .js 文件)
↓
浏览器加载成品
对于”单 HTML 文件 + 离线可用”的场景,手动下载是唯一合理的选择。
4.5 对比总结
| 维度 | 手动下载 | npm install |
|---|---|---|
| 操作方式 | 浏览器右键另存为 | 命令行执行 |
| 得到什么 | 1 个 .min.js 文件 |
node_modules/ 文件树 |
| 文件大小 | ~500KB(已压缩) | 几 MB(未压缩源码) |
| 浏览器能直接用 | 能 | 不能 |
| 依赖管理 | 无 | 有 |
| 需要 Node.js | 不需要 | 需要 |
| 适合纯浏览器项目 | 适合 | 不适合 |
五、文件大小:为什么 JS 库这么小
5.1 真实数据
| 库 | 功能 | 未压缩 | .min.js 压缩后 |
|---|---|---|---|
| math.js | 科学计算 | ~2.5MB | ~500KB |
| Chart.js | 图表 | ~700KB | ~200KB |
| jQuery | DOM 操作 | ~300KB | ~90KB |
| Tailwind CSS | 样式 | ~3.8MB | ~300KB(精简版) |
| three.js | 3D 渲染 | ~1.2MB | ~600KB |
| marked.js | Markdown | ~40KB | ~25KB |
math.js 的 500KB 在 JS 库中算偏大的,因为功能确实多。大部分库压缩后只有几十到两百 KB。
5.2 500KB 是什么概念
一张手机随手拍的照片 → 3~5 MB
一页微信朋友圈图片 → 1~2 MB
math.js 压缩文件 → 0.5 MB
一首 MP3 歌曲(128kbps) → 3~4 MB
一个 10 秒短视频 → 5~20 MB
500KB 比发一张朋友圈图片还小。
5.3 .min.js 做了什么
.min.js 对源码做了三件事,不损失任何功能:
1. 删除所有注释 → 可能减少 30%
2. 删除所有空格和换行 → 可能减少 20%
3. 长变量名改为单字母 → 可能减少 10~20%
示例:
// 未压缩(给人看,~2.5MB)
function add(a, b) {
// 将两个数相加
// 参数 a - 第一个数
// 参数 b - 第二个数
var result = a + b;
return result;
}
// 压缩后(给机器看,~0.5MB)
function add(a,b){return a+b}
5.4 网络传输时还会更小
math.min.js 磁盘大小 → 500 KB
实际网络传输(gzip 压缩后) → ~150 KB
CDN 服务器和浏览器之间自动协商 gzip 压缩,纯文本压缩率极高,实际传输量约为磁盘大小的三分之一。
5.5 为什么比 Python 包小这么多
核心原因:Python 包里装了编译好的机器码,JS 包里只有纯文本。
Python 的科学计算库(NumPy 等)底层用 C 语言编写,pip 安装时下载的是编译好的二进制文件(.so / .pyd),体积大。而且需要为 Windows、Mac、Linux 分别编译不同版本。
JS 的 math.js 从头到尾就是纯文本(JavaScript 源码),用记事本就能打开看。
Python 包 = 买一台组装好的自行车(占地方,拿起来就能骑)
JS 包 = 拿一张自行车图纸(几乎不占地方,但需要有工厂来造)
而浏览器,就是那个免费的造车工厂
5.6 为什么 JS 纯文本也能跑得快
因为浏览器本身极其庞大(Chrome 几百 MB),其中的 V8 引擎是用 C++ 写的高性能 JIT(即时编译)引擎。V8 会在零点几秒内把 JS 纯文本编译成高度优化的机器码。
Python 模式:Python 解释器(小) + NumPy(自带C发动机) → 30MB + 15MB = 45MB 才能算
JS 模式: Chrome(自带V8超级发动机,几百MB) + math.js(图纸,0.5MB) → 瞬间编译,开始算
math.js 0.5MB 就能实现接近 NumPy 的功能,不是作者有魔法,而是站着巨人的肩膀上——几百 MB 的浏览器替它把重活都干了。
5.7 pip install numpy 下载的 15MB 里有什么
实际计算用的机器码(C 编译产物) ~5 MB
C 语言头文件(给开发者参考) ~3 MB
测试用例 ~2 MB
文档和元数据 ~1 MB
多平台版本(Win/Mac/Linux 都打包) ~4 MB
用户只用到了矩阵相加功能,但 15MB 全下载了。而 math.min.js 是精心切割的最终成品,只有纯粹的计算逻辑。
六、浏览器作为运行基座
6.1 核心认知
现代浏览器是一个兼容所有符合语法规则的 .js 文件的巨大运行基座。只要是合法的 JavaScript 代码,扔进浏览器就能跑,不需要额外的编译器、链接器、环境配置。
6.2 浏览器内建的能力
浏览器天然自带了大量能力,JS 库不需要把这些打包进去:
| 能力 | 浏览器内建 API | Python 需要装的包 |
|---|---|---|
| 画图 | Canvas 2D / WebGL | matplotlib (~15MB) |
| 网络请求 | fetch / XMLHttpRequest | requests (~1MB) |
| JSON 处理 | JSON.parse / stringify | json(内置) |
| 界面渲染 | DOM | tkinter / PyQt (~几十MB) |
| 样式系统 | CSS | 无对应概念 |
| 本地存储 | localStorage / IndexedDB | sqlite3 等 |
这就是为什么一个 500KB 的 math.js 加上浏览器,就能实现 Python 需要装几十 MB 包才能做到的事。
七、JavaScript 的能力边界
浏览器的设计哲学是:“我允许你在我这里跑代码,但你休想碰我外面的东西。” 这叫沙箱机制。
7.1 完全做不到的(安全沙箱的铁壁)
| 想做的事 | 为什么做不到 |
|---|---|
| 读写本地任意文件 | 没有文件系统访问权限 |
| 调用本地程序(启动 Python、打开 Word) | 不能执行系统命令 |
| 直接操作硬件(串口、PCIe、底层打印机) | 没有硬件驱动接口(USB/蓝牙有受限 API,不通用) |
| 关机、重启、改系统设置 | 沙箱与操作系统完全隔离 |
| 关掉浏览器后继续运行 | 标签页关闭 → 内存释放 → 代码立刻死亡 |
| 做服务端(监听端口、等待连接) | 浏览器是客户端,只能主动请求,不能被动监听 |
这些限制是故意设计的,不是技术做不到。如果网页 JS 能读写硬盘,打开任何网站别人就能偷走文件——这不可接受。
7.2 能做到但很痛苦的(语言设计的历史包袱)
单线程——算力大户的噩梦
Python:开 10 个进程,10 个 CPU 核心一起算
JS(浏览器):只有一个主线程,所有事情排队做
如果算一个 10000×10000 的矩阵求逆:点击”计算” → 主线程开始算 → 整个网页冻死(鼠标点不动、按钮按不了)→ 几秒后算完 → 网页复活。因为画界面和算数学用的是同一个线程。
补救方案是 Web Worker(开后台线程),但 Worker 里不能用 DOM,主线程和 Worker 之间传大数据要拷贝,代码复杂度翻倍。
数字精度——没有原生高精度浮点数
// JavaScript
0.1 + 0.2 // 0.30000000000000004(不是精确的 0.3)
JS 原生只有 64 位双精度浮点数(IEEE 754),没有 Python 的 Decimal 类型。有效数字超过 15 位就不准确了。金融计算(要求分毫不差)或超高精度科学计算会碰到天花板。
异步模型——认知门槛
因为单线程,所有”耗时操作”都必须”挂起”(回调/Promise/async-await),对没有异步经验的人来说是一堵认知墙。
7.3 能做到但有天花板的(科学计算最可能碰到的)
大规模矩阵运算——没有 BLAS 加速
Python NumPy:底层调用 C 语言的 BLAS/LAPACK(Intel/AMD 花几十年优化的库)
矩阵乘法自动用 CPU 的 SIMD 指令(一次算 8 个数)
1000×1000 矩阵乘法 → 几毫秒
JS math.js:纯 JavaScript 的 for 循环嵌套实现
1000×1000 矩阵乘法 → 几百毫秒到几秒
- 小矩阵(< 500×500):完全没问题
- 大矩阵(> 2000×2000):明显变慢,可能卡顿
- 超大规模(深度学习级):纯 JS 基本不可能
符号计算——力不从心
math.js 能做简单的符号求导:derivative("x^3 + sin(x)", "x") → 3*x^2 + cos(x) ✅
但做不到:
- 积分出解析式(∫ e^(-x²) dx = ?)❌
- 解微分方程 ❌
- 多项式因式分解 ❌
- 矩阵的 Jordan 标准型 ❌
这些需要专门的计算机代数系统(如 Python 的 SymPy),math.js 的定位只是”增强版计算器”。
7.4 能力边界全景图
完全做不了 能做但慢 轻松搞定
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 读写本地文件 │ │ 2000×2000 矩阵 │ │ 100×100 矩阵运算 │
│ 调用本地程序 │ │ 高精度浮点运算 │ │ 方程求解 │
│ 关机后继续跑 │ │ 符号积分 │ │ 数值积分/微分 │
│ 直接操作硬件 │ │ 深度学习训练 │ │ 函数绘图 │
│ │ │ │ │ 统计分析 │
│ 这是安全墙 │ │ 这是性能墙 │ │ 这是主场 │
└───────────────┘ └─────────────────┘ └─────────────────┘
八、适用场景判断
适合用纯前端做的
- 计算器、表达式求值
- 中小规模矩阵运算(< 1000 阶)
- 数值求解方程
- 数值积分 / 数值微分
- 函数绘图
- 描述性统计、简单回归
- 教学演示工具
- 需要”打开即用、零安装”的任何场景
- 需要美观界面的交互式工具
不适合用纯前端做的
- 超大规模数值计算(大型 FEM、CFD)
- 需要高精度浮点的金融计算
- 符号计算(解析积分、微分方程)
- 需要读写本地文件的场景
- 需要长时间后台运行的任务
- 深度学习模型训练
如果需要突破浏览器限制
| 想突破的限制 | 方案 | 代价 |
|---|---|---|
| 离线使用 | 手动下载 .min.js 到本地 | 无 |
| 大规模计算性能 | WebAssembly(把 C/Rust 编译成 .wasm) | 开发复杂度大幅增加 |
| 读写本地文件 | File System Access API(仅 Chromium 浏览器) | 兼容性差,需要用户授权 |
| 后台运行 | Service Worker(仅限网页自身范围) | 能力极其有限 |
| 突破所有限制 | 用 Electron/Tauri 打包成桌面应用 | 失去”打开 HTML 即用”的简洁性 |
九、关键术语速查
| 术语 | 一句话解释 |
|---|---|
| CDN | 内容分发网络,全球分布的”代码图书馆”,提供公开的 JS/CSS 文件下载 |
.min.js |
压缩版 JS 文件,去掉了注释、空格、缩短了变量名,体积更小,功能不变 |
| gzip | 一种文本压缩格式,CDN 传输时自动使用,能把文本再压缩到原来的 1/3 |
| 沙箱 | 浏览器的安全隔离机制,JS 代码只能在内部分配的内存中运行,不能接触外部系统 |
| V8 | Chrome 内置的 JavaScript 引擎,用 C++ 写的,能把 JS 即时编译成高速机器码 |
| JIT | Just-In-Time,即时编译,运行时把代码编译成机器码(区别于提前编译 AOT) |
| Web Worker | 浏览器提供的后台线程 API,可以把计算放到后台,避免界面卡死 |
| SIMD | 单指令多数据流,CPU 的一次指令同时处理多个数据,NumPy 底层用它加速 |
| BLAS/LAPACK | 底层线性代数计算库,几十年优化的成果,NumPy 底层调用的就是它们 |
| Node.js | 让 JavaScript 能脱离浏览器在命令行运行的环境,有文件系统、网络等能力 |
| npm | Node.js 的包管理器,相当于 Python 的 pip |
文档整理自对话记录,作为个人知识库存档。 ```
直接复制保存为 .md 文件即可。如果用 VS Code 或 Typora 打开,表格和代码块都会有正确的渲染效果。