Parcel 的口号是”零配置”。你把一个 HTML 文件丢给它,它会自动分析里面的 script、link 标签,找到依赖的 JS、CSS,递归解析,最后输出打包结果。不需要写任何配置文件。
这听起来像是玩具,但 Parcel 的设计其实很认真。它用 Rust 重写了核心解析器,用多线程并行处理,还有持久化缓存。对于中小型项目,开发体验相当舒服。
快速上手#
安装:
npm install parcel --save-dev假设你的项目结构是这样的:
my-app/├── src/│ ├── index.html│ ├── index.js│ └── styles.css└── package.jsonindex.html:
<!DOCTYPE html><html> <head> <link rel="stylesheet" href="./styles.css" /> </head> <body> <div id="app"></div> <script type="module" src="./index.js"></script> </body></html>index.js:
import './styles.css'
console.log('Hello Parcel!')document.getElementById('app').innerHTML = '<h1>Parcel 真香</h1>'package.json:
{ "name": "my-app", "source": "src/index.html", "scripts": { "dev": "parcel", "build": "parcel build" }, "devDependencies": { "parcel": "^2.12.0" }}运行 npm run dev,Parcel 会启动开发服务器,自动打开浏览器。改任何文件都会热更新。
运行 npm run build,产物输出到 dist/ 目录。
整个过程没写一行配置。
核心特性#
自动依赖解析#
Parcel 会从入口文件开始,自动发现所有依赖:
- HTML 中的
<script>、<link>、<img>等标签 - JavaScript 中的
import、require、import()动态导入 - CSS 中的
@import、url()引用 - 各种资源文件的相互引用
你不需要告诉它”这个项目用了什么”,它自己会分析出来。
多线程处理#
Parcel 用 Rust 写的 @parcel/rust 处理核心的解析工作,JavaScript 部分也做了多线程优化。在多核机器上,构建速度会随 CPU 核心数提升。
持久化缓存#
构建结果会缓存到 .parcel-cache 目录。二次构建时,只有变化的文件会重新处理,没变的直接用缓存。
冷启动可能要几秒,热启动几乎是瞬间的。
开箱即用的支持#
不需要额外配置就能处理:
| 类型 | 支持情况 |
|---|---|
| TypeScript | ✅ 自动识别 .ts/.tsx |
| React/JSX | ✅ 自动识别 .jsx/.tsx |
| Vue | ✅ 需要安装 @parcel/transformer-vue |
| CSS/Sass/Less | ✅ 自动处理 |
| CSS Modules | ✅ .module.css 自动识别 |
| 图片/字体 | ✅ 自动处理引用 |
| JSON/YAML | ✅ 直接 import |
| WebAssembly | ✅ 支持导入 .wasm |
代码分割#
动态导入自动触发代码分割:
// 点击按钮时才加载这个模块button.addEventListener('click', async () => { const { heavyFunction } = await import('./heavy-module.js') heavyFunction()})Parcel 会把 heavy-module.js 单独打包成一个 chunk。
Tree Shaking#
生产构建时自动移除未使用的代码:
export function add(a, b) { return a + b}export function subtract(a, b) { return a - b}
// index.js - 只用了 addimport { add } from './math.js'console.log(add(1, 2))// subtract 会被移除配置方式#
虽然号称零配置,但实际项目总有定制需求。Parcel 支持多种配置方式。
package.json 配置#
最常用的配置直接写在 package.json 里:
{ "name": "my-app", "source": "src/index.html", "targets": { "default": { "distDir": "dist", "publicUrl": "/", "context": "browser", "engines": { "browsers": "> 0.5%, last 2 versions, not dead" } } }, "browserslist": "> 0.5%, last 2 versions, not dead"}.parcelrc 配置#
更复杂的配置用 .parcelrc 文件:
{ "extends": "@parcel/config-default", "transformers": { "*.svg": ["@parcel/transformer-svg-react"] }, "reporters": ["...", "parcel-reporter-bundle-analyzer"]}"..." 表示继承默认配置,然后在后面添加。
常用配置场景#
修改输出目录:
{ "targets": { "default": { "distDir": "build" } }}多入口构建:
{ "source": ["src/index.html", "src/admin.html"]}环境变量:
创建 .env 文件:
API_URL=https://api.example.com在代码中使用:
console.log(process.env.API_URL)Parcel 会自动读取 .env、.env.local、.env.production 等文件。
指定 Node 版本:
{ "targets": { "default": { "engines": { "node": ">=18" } } }}插件系统#
Parcel 的插件分为几类:
Transformer#
处理特定类型的文件:
{ "extends": "@parcel/config-default", "transformers": { "*.svg": ["@parcel/transformer-svg-react"], "*.graphql": ["parcel-transformer-graphql"] }}常用的 transformer:
| 插件 | 用途 |
|---|---|
| @parcel/transformer-svg-react | SVG 转 React 组件 |
| @parcel/transformer-vue | Vue SFC 支持 |
| @parcel/transformer-mdx | MDX 文件支持 |
| parcel-transformer-graphql | GraphQL 文件支持 |
Resolver#
自定义模块解析逻辑:
{ "extends": "@parcel/config-default", "resolvers": ["parcel-resolver-ts-paths", "..."]}Reporter#
构建过程的报告和分析:
{ "extends": "@parcel/config-default", "reporters": ["...", "parcel-reporter-bundle-analyzer"]}运行 parcel build 后会生成 bundle 分析报告。
Optimizer#
优化输出产物:
{ "extends": "@parcel/config-default", "optimizers": { "*.js": ["@parcel/optimizer-swc"] }}React 项目实战#
创建一个完整的 React 项目:
mkdir react-parcel-appcd react-parcel-appnpm init -ynpm install react react-domnpm install parcel --save-devsrc/index.html:
<!DOCTYPE html><html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>React Parcel App</title> </head> <body> <div id="root"></div> <script type="module" src="./index.tsx"></script> </body></html>src/index.tsx:
import { createRoot } from 'react-dom/client'import { App } from './App'import './styles.css'
const root = createRoot(document.getElementById('root')!)root.render(<App />)src/App.tsx:
import { useState } from 'react'
export function App() { const [count, setCount] = useState(0)
return ( <div className="app"> <h1>Hello Parcel + React</h1> <button onClick={() => setCount((c) => c + 1)}>点击次数: {count}</button> </div> )}src/styles.css:
.app { max-width: 800px; margin: 0 auto; padding: 2rem; text-align: center;}
button { padding: 0.5rem 1rem; font-size: 1rem; cursor: pointer;}package.json:
{ "name": "react-parcel-app", "source": "src/index.html", "scripts": { "dev": "parcel", "build": "parcel build", "clean": "rm -rf dist .parcel-cache" }, "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "parcel": "^2.12.0" }}运行 npm run dev,就可以开发了。TypeScript 和 JSX 都是自动支持的。
Vue 项目配置#
Vue 需要额外安装 transformer:
npm install vuenpm install @parcel/transformer-vue --save-dev.parcelrc:
{ "extends": "@parcel/config-default", "transformers": { "*.vue": ["@parcel/transformer-vue"] }}src/App.vue:
<template> <div class="app"> <h1>Hello Parcel + Vue</h1> <button @click="count++">点击次数: {{ count }}</button> </div></template>
<script setup lang="ts">import { ref } from 'vue'
const count = ref(0)</script>
<style scoped>.app { text-align: center; padding: 2rem;}</style>src/index.ts:
import { createApp } from 'vue'import App from './App.vue'
createApp(App).mount('#app')性能优化#
利用缓存#
Parcel 的缓存默认开启,但你可以调整缓存行为:
# 清除缓存rm -rf .parcel-cache
# 禁用缓存(调试用)parcel build --no-cache排除大型依赖#
通过配置排除不需要打包的依赖:
{ "targets": { "default": { "externals": { "react": "React", "react-dom": "ReactDOM" } } }}然后在 HTML 中用 CDN 引入。
Scope Hoisting#
生产构建默认开启,会把多个模块合并到同一个作用域,减少函数调用开销:
# 默认开启parcel build
# 禁用(调试用)parcel build --no-scope-hoist压缩配置#
Parcel 默认用 SWC 压缩 JavaScript,用 cssnano 压缩 CSS。可以自定义:
{ "extends": "@parcel/config-default", "optimizers": { "*.js": ["@parcel/optimizer-terser"] }}常见问题#
1. 端口冲突#
# 指定端口parcel --port 8080
# 或者parcel -p 80802. 热更新不工作#
检查是否有语法错误。Parcel 的 HMR 在遇到错误时可能会回退到刷新整个页面。
也可以试试清除缓存:
rm -rf .parcel-cachenpm run dev3. TypeScript 类型错误没提示#
Parcel 不做类型检查,只做语法转换。需要单独配置:
{ "scripts": { "dev": "parcel", "typecheck": "tsc --noEmit --watch", "build": "tsc --noEmit && parcel build" }}4. 第三方库报错#
有些库没有正确的 package.json 配置,Parcel 可能解析失败。可以尝试:
{ "alias": { "problematic-lib": "problematic-lib/dist/index.js" }}5. 图片路径问题#
生产构建后图片 404,检查 publicUrl 配置:
{ "targets": { "default": { "publicUrl": "./" } }}如果部署到子路径:
{ "targets": { "default": { "publicUrl": "/my-app/" } }}6. 构建产物太大#
用 bundle analyzer 分析:
npm install parcel-reporter-bundle-analyzer --save-dev.parcelrc:
{ "extends": "@parcel/config-default", "reporters": ["...", "parcel-reporter-bundle-analyzer"]}与其他工具对比#
| 对比项 | Parcel | Webpack | Vite | esbuild |
|---|---|---|---|---|
| 配置复杂度 | 极简 | 复杂 | 简单 | 简单 |
| 冷启动 | 中等 | 慢 | 快 | 极快 |
| 热更新 | 快 | 中等 | 快 | 无 |
| 插件生态 | 一般 | 丰富 | 丰富 | 发展中 |
| 零配置程度 | 最高 | 低 | 中等 | 低 |
| 适用场景 | 中小型项目 | 所有项目 | 现代项目 | 库、简单项目 |
什么时候用 Parcel#
Parcel 适合这些场景:
- 原型开发:快速搭建 demo,不想折腾配置
- 中小型项目:功能够用,配置简单
- 学习用途:专注业务逻辑,不被构建工具分心
- 静态网站:多页面应用,HTML 作为入口
不太适合的场景:
- 大型复杂项目:可能需要更细粒度的控制
- 微前端:需要 Module Federation 等高级功能
- 需要深度定制:Webpack 的插件生态更完善