从 HTML 到 Vite:现代前端开发到底解决了什么问题?
前言
前端开发并不是一开始就复杂的。
最开始,我们只需要写 HTML、CSS 和 JavaScript,就能做出一个可以运行的网页。
但随着项目变大,文件会越来越多,代码之间的依赖关系会越来越复杂,本地开发、上线构建、浏览器缓存等问题也会陆续出现。
这篇文章就从一个普通网页的组成讲起,一步步说明:为什么现代前端需要模块化,为什么需要构建工具,以及 Vite 到底解决了什么问题。
从最简单的网页开始
一个普通网页通常由三部分组成:HTML、CSS 和 JavaScript。
HTML 负责页面结构,CSS 负责页面样式,JavaScript 负责交互逻辑。
一个很小的页面,可以把它们都写在同一个 HTML 文件里;但项目稍微变大,这个文件就会越来越难维护。
所以更常见的做法,是把它们拆成不同文件:
<link rel="stylesheet" href="style.css" /><script src="main.js"></script>这样做之后,结构、样式和逻辑被分开了,代码确实更清晰。但新的问题很快出现:当 JavaScript 文件越来越多时,脚本的加载顺序会变得非常重要。
比如 a.js 里定义了一个变量,b.js 里要使用它,那么 a.js 就必须写在 b.js 前面。顺序一旦错了,页面运行时就可能报错:变量不存在、函数未定义。
如果项目里只有两三个脚本,这个问题还可以靠人脑记住。可如果项目里有几十个甚至上百个 JS 文件,手动维护它们的引用顺序就会非常痛苦。
模块化:让文件之间的关系写清楚
为了解决这个问题,现代前端引入了一个非常重要的基础能力:模块化。
模块化的核心思想是:每个文件只暴露自己想让外部使用的内容,其他文件需要什么,就显式地导入什么。
例如:
export function add(a, b) { return a + b}import { add } from './utils.js'
console.log(add(1, 2))这样一来,文件之间的依赖关系就不再依靠 HTML 里的 <script> 顺序来维持,而是直接写在 JavaScript 代码里。
这就是 ES Modules,也就是我们常说的 ESM。export 负责导出,import 负责导入。
模块化带来的第一个变化:不能再直接双击打开 HTML
使用普通 <script> 标签时,很多简单页面可以直接双击 HTML 文件运行,也就是通过 file:// 协议打开。
但如果你使用了 ESM:
<script type="module" src="main.js"></script>情况就不一样了。
浏览器在加载模块时,会按照更严格的规则处理文件请求。通过 file:// 打开页面时,模块之间的导入很容易因为同源策略或 CORS 限制而加载失败。
所以,使用模块化之后,我们通常不能再依赖“直接双击 HTML 文件”来开发页面,而是需要通过一个本地 HTTP 服务来访问页面。
也就是说,页面应该通过类似这样的地址打开:
http://localhost:5173而不是:
file:///C:/project/index.html项目变大后,还会遇到哪些问题?
到这里,我们其实已经从“写几个静态文件”进入了“现代前端项目”的世界。
这个阶段通常会遇到三个典型问题。
1. 本地开发需要一个服务器
浏览器中的原生 ESM 开发通常应通过 HTTP 服务访问。如果每次修改代码后,都要手动上传到服务器再刷新页面,开发效率会非常低。
理想的开发体验应该是:在本地启动一个服务器,修改代码后浏览器自动刷新,甚至只更新变动的部分。
2. 上线时不能让浏览器请求太多零散文件
开发阶段,我们希望文件拆得清楚,方便维护。
但上线之后,如果页面需要请求大量 CSS 和 JS 文件,首屏加载可能会变慢。虽然现代浏览器和 HTTP/2 已经缓解了这个问题,但生产环境仍然需要压缩、拆分和优化资源。
3. 浏览器缓存可能导致用户拿到旧文件
浏览器会缓存 JS、CSS、图片等静态资源。这样可以加快第二次访问速度,但也会带来一个问题:代码明明已经更新了,用户浏览器里却还在使用旧文件。
一个常见解决方案是给文件名加上 hash,也叫文件指纹。
例如:
main.8d3f4a1c.jsstyle.6a91b2ef.css只要文件内容发生变化,hash 就会变化,文件名也会变化。浏览器会把它当成一个新文件重新下载。
Vite:现代前端开发的基础工具
为了解决上面这些问题,我们会引入构建工具。现在非常常用的一个构建工具,就是 Vite。
需要注意的是,Vite 不是 React、Vue 这种 UI 框架。它不负责规定页面怎么写,而是负责开发服务器、模块处理和生产构建。
Vite 主要做两类事情:开发阶段提升效率,生产阶段优化资源。
开发阶段:启动本地服务器
Vite 会在本地启动一个开发服务器。你可以通过 http://localhost:5173 访问项目,而不是直接双击 HTML 文件。
这样就解决了 ESM 在 file:// 协议下的加载问题。
同时,Vite 还支持热更新,也就是 HMR。修改代码后,浏览器可以快速看到变化,不需要你手动重新部署。
生产阶段:打包、压缩、加文件指纹
开发时,我们希望代码拆得细,文件职责清楚。
上线时,我们希望资源更适合浏览器加载。Vite 在构建生产版本时,会对代码进行打包、压缩和优化,并为输出文件生成带 hash 的文件名。
最终生成的文件通常会放在 dist 目录中。这个目录里的内容,才是要部署到服务器上的生产版本。
为什么使用 Vite 前要安装 Node.js?
Vite 本质上是一个运行在 Node.js 环境里的前端工具。
JavaScript 原本主要运行在浏览器里,用来操作页面。但如果我们想在浏览器之外运行 JavaScript,比如执行构建工具、启动开发服务器、安装第三方库,就需要 Node.js。
你可以把 Node.js 理解成“让 JavaScript 在电脑本地运行的环境”。
安装 Node.js 后,可以在终端里检查版本:
node -vnpm -v如果能看到版本号,就说明安装成功了。
这里的 npm 是 Node.js 自带的包管理工具。它的作用类似于 Python 里的 pip,用来安装和管理 JavaScript 生态里的第三方库。
如何在项目里安装 Vite?
进入项目文件夹后,先初始化项目:
npm init -y执行后,项目里会出现一个 package.json 文件。它用来记录项目名称、版本、脚本命令、依赖库等信息。
然后安装 Vite:
npm install -D vite这里的 -D 表示把 Vite 安装为开发依赖,也就是写入 devDependencies。
所谓“开发依赖”,不是说它只能安装在你自己的电脑上,而是说它只在开发和构建阶段需要。
如果选择在服务器上执行 npm run build,服务器在构建阶段也需要安装 Vite,所以这时要安装 devDependencies。默认执行 npm install 会安装它们。
但网站真正运行起来以后,浏览器访问的是 dist 里的静态文件,Nginx 只负责把这些文件返回给浏览器,并不需要继续运行 Vite。
安装完成后,项目里通常会多出两个东西:
node_modules/package-lock.json
node_modules 是第三方库实际存放的位置。Vite 以及 Vite 依赖的其他库,都会安装在这里。

package-lock.json 用来锁定依赖版本,保证其他人安装项目依赖时,尽量得到一致的结果。
补充一下,如果是从零创建一个新项目,也可以直接使用 Vite 官方脚手架:
npm create vite@latest它会帮你生成基础项目结构。本文使用 npm init 和 npm install -D vite,是为了更清楚地说明 Vite 是如何作为项目依赖被安装进来的。
Vite 命令从哪里来?
安装 Vite 后,node_modules/.bin 目录里会出现可执行命令。
在 Windows 下,你可能会看到类似这样的文件:
node_modules\.bin\vite.cmd它就是本地项目里的 Vite 命令。

不过实际开发中,我们通常不会手动去执行这个路径,而是把命令写进 package.json 的 scripts 里。
例如:
{ "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }}之后就可以这样使用:
npm run devnpm run buildnpm run previewnpm run dev 用来启动本地开发服务器。启动成功后,终端会显示本地访问地址,例如 http://localhost:5173/。

npm run build 用来生成生产环境文件。
npm run preview 用来在本地启动一个服务器,检查构建后的生产版本是否正常;它不是正式的生产服务器。
补充:构建后的 dist 如何部署到服务器?
一种常见做法是:服务器拉取源码后,在服务器上安装依赖并执行构建,而不是把 dist 提交到 Git 仓库。
具体来说,Git 仓库里只保存项目源码和配置文件,dist 和 node_modules 都放进 .gitignore。
node_modules 不提交,是因为它体积很大,而且可以根据 package.json 和 package-lock.json 重新安装。
dist 不提交,是因为它是构建产物,可以随时通过源码重新生成。
所以一次典型的服务器部署流程是:
git pullnpm installnpm run build注意:如果服务器负责执行 npm run build,就不能在构建前只安装 production 依赖。因为 Vite 在 devDependencies 中,构建阶段仍然需要它。
执行完 npm run build 后,服务器上会生成新的 dist 目录。
然后在 Nginx 中把网站根目录指向这个 dist 文件夹,并把入口文件设置为 index.html。
例如:
server { listen 80; server_name example.com;
root /var/www/my-project/dist; index index.html;}这样浏览器访问网站时,Nginx 返回的就是 Vite 构建后的生产文件。
如果后续项目使用前端路由,例如 React Router,还需要让 Nginx 在找不到具体文件时回退到 index.html。
总结
现代前端开发并不是一开始就复杂的。它是项目规模变大后,为了解决实际问题一步步演化出来的。
最开始,我们只需要 HTML、CSS 和 JavaScript。
后来,文件越来越多,手动维护脚本顺序变得困难,于是有了模块化。
模块化让依赖关系更清楚,但也要求我们通过 HTTP 服务来开发页面,于是需要本地开发服务器。
项目上线时,还需要打包、压缩、文件指纹和缓存处理,于是构建工具就变得非常重要。
Vite 解决的正是这些问题:开发时提供快速的本地服务器和热更新,上线前生成优化后的生产资源。
所以,理解 Vite 的关键不是记命令,而是理解它为什么出现:它是在帮我们把“适合开发的代码”,转换成“适合上线的资源”。
学完 Vite 之后,下一步学什么
学到这里,我们已经解决了一部分“工程化”的问题。
Vite 帮我们解决的是开发和构建:本地服务器、模块化、热更新、打包、压缩、文件指纹。
也就是说,Vite 主要回答的是:
项目怎么开发?代码怎么组织成模块?上线前怎么构建成生产资源?但 Vite 并不会直接解决“页面本身如何组织”的问题。
当页面越来越复杂时,我们会遇到新的麻烦:按钮、表单、列表、弹窗、用户状态、加载状态都混在一起。如果继续手动操作 DOM,代码会很快变得难以维护。
这时就需要 React。
React 是一个用于构建用户界面(UI)的 JavaScript 库,也常被称为前端框架;它的核心思想是用组件来组织页面。
React 关注的是另一个层面:如何把页面拆成组件,如何让数据传入组件,如何让状态变化后页面自动更新。
所以,Vite 和 React 解决的是两个不同问题:
Vite:解决项目开发、构建、部署的问题React:解决页面组织、数据驱动、状态更新的问题也正因为如此,学完 Vite 之后,继续学习 React 是很自然的一步。
这条现代前端学习路线可以继续往下走:
HTML/CSS/JS→ Vite→ React(或 Vue、Svelte 等)→ Next.js 等应用框架支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!