为什么需要模块化
传统 script 引入
<script src="js/nav.js"></script> 引入的是本地文件的相对路径,含义是把当前目录下 js 目录中的 nav.js 引入页面。
<script> 标签也可以引用发布在互联网上的 .js 文件:
<script src="https://cdn.jsdelivr.net/npm/animejs@4/lib/anime.iife.min.js"></script>
上面这个地址指向 anime.js——一个专门提供动画方案的现成脚本。这类由他人编写、发布到互联网上供复用的脚本,统称为第三方库。
全局污染
cards.js 依赖 anime.animate、anime.stagger 这些名字才能运行。这是传统 <script> 引入方式的固有约定:库向页面全局提供自己的名字,使用者从全局取用。
随着项目变大,引入的库不再只有一个——图表库、日期处理库、ajax 工具库……每个库都靠”往全局挂名字”的方式提供功能。而 JavaScript 的全局空间中,一个名字只能存一个值:一旦两个库挂载了同名成员,后挂载的会静默覆盖先挂载的,没有任何警告。
这种现象称为全局污染(global pollution)。小项目尚能靠记忆规避;项目一旦长大,撞名只是时间问题,而每一次撞名都是一个难以排查的 bug。
出路:工程化
<script> 手动排序与全局污染表面上是两个问题,根源相同:
“全部堆在一起 + 全局名称(变量)+ 手动排序”的传统组织方式,本来就不是为严肃项目设计的。
这不是工程师水平的问题,而是工具本身达到了能力上限。全球前端开发者都撞过这堵墙,并由此发展出一整套跨越它的方法,统称工程化。工程化的第一招,就是模块化。
模块化与 ES 模块
export 与 import
模块化与传统方式只在一个关键点上不同:共享的方式。
| 传统方式 | 模块化 | |
|---|---|---|
| 可见性 | 代码默认”全世界都看得见”(都堆在 window 全局空间里) | 代码默认”只有自己看得见” |
| 提供功能 | 库向全局挂名,共享靠”公共” | 必须显式标注 export,共享靠”明文声明” |
| 使用功能 | 直接从全局取用 | 必须显式写 import,不写就用不到 |
export 与 import 是模块化的两个核心关键字:
export:给文件内的某个成员打上”对外可用”的标记;未标记的,外部拿不到。import:在文件顶部明文声明”要使用某个文件里的某个成员”;不声明的,用不到。
名词:ES 与 ECMAScript
ES 是 ECMAScript 的缩写,即 JavaScript 这门语言的官方标准规范。所有浏览器、所有 JS 引擎都必须按这份规范实现 JavaScript。日常所说的”JavaScript”,本质上就是”按 ECMAScript 规范实现的那套东西”。
由此派生两个常见说法:
- ES 模块:JavaScript 标准中定义的那套模块系统。它不是另一种语言,也不是新东西,而是 JavaScript 自带的官方模块方案。
- ES6:ECMAScript 的第 6 版(2015 年发布)。
import/export正式定义于这一版。
注意:ES 模块文件必须通过服务器提供才能加载,浏览器不允许直接从
file://读取。
构建工具
三方诉求的矛盾
工程治理、开发体验、用户体验,三方在互相拉扯:
- 工程治理:希望项目规范、安全、好维护、好扩展(模块化、拆文件都是为此服务);
- 开发者:希望写得简单、顺手、舒服;
- 浏览器与最终用户:希望加载更快、资源更少、性能更高。
三者常常彼此冲突:为了规范牺牲开发便利,为了开发便利又影响运行效率,很难同时满足。构建工具就是化解这一矛盾的方案——让一类工具夹在中间做”翻译”:工程的规矩照守,开发者按自己舒服的方式编写,最后由工具把源码转换成浏览器想要的样子。这个转换过程叫前端构建,这类工具叫构建工具(build tool)。
构建工具的职责
- 本地开发服务器 + 热更新:改完一保存,浏览器自动更新,无需手动刷新;
- 合并碎文件:多个 CSS 合成 1 个、多个 JS 模块合成 1 个,请求数从十几个降到几个;
- 文件名加 hash 指纹:内容一变、文件名就变,浏览器立刻知道需要重新拉取,缓存随之失效。
npm:项目级包管理
npm 与 apt 的区别
npm 与 apt 都是”应用商店”,但管辖范围不同:
- apt 给整台电脑装东西:装一个 nginx,全系统都能用。
- npm 默认给当前项目装东西:A 项目用 anime.js 4.4、B 项目用 anime.js 3.0,两边各装各的、互不干扰。
正因为按项目管理,使用前必须先明确声明”这里有一个项目”,这个动作叫初始化。
初始化与安装
在项目根目录(与 index.html 同级)运行:
npm init -y
该命令生成 package.json 项目档案。有了 npm 与 package.json,安装 Vite 只需一行(在 index.html、package.json 同级目录运行):
npm install -D vite
-D即--save-dev,表示把 Vite 装成开发依赖——只在开发阶段使用,不进入最终产物。
Vite 实操:dev / build / preview
Vite 的可执行文件位于 node_modules/.bin/vite,通过该路径直接运行。
dev:开发服务器
./node_modules/.bin/vite
随改随看,保存即热更新。
build:打包上线版本
./node_modules/.bin/vite build
运行后生成 dist 目录:
dist/
├── index.html
└── assets/
├── main-Beya6efK.css ← 多个 CSS 合并成的 1 个文件
└── main-_go62SDo.js ← 多个 JS 模块合并成的 1 个文件
preview:上线前本地预览
./node_modules/.bin/vite preview
启动一个专门伺服 dist 的服务器。打开后按 F12 查看 Network 面板——干净利落,只有寥寥几个请求。
마음에 드신다면, 댓글을 남겨주세요~