Skip to content

Parcel - 零配置的极速打包工具

Parcel 的口号是”零配置”。你把一个 HTML 文件丢给它,它会自动分析里面的 script、link 标签,找到依赖的 JS、CSS,递归解析,最后输出打包结果。不需要写任何配置文件。

这听起来像是玩具,但 Parcel 的设计其实很认真。它用 Rust 重写了核心解析器,用多线程并行处理,还有持久化缓存。对于中小型项目,开发体验相当舒服。

快速上手#

安装:

Terminal window
npm install parcel --save-dev

假设你的项目结构是这样的:

my-app/
├── src/
│ ├── index.html
│ ├── index.js
│ └── styles.css
└── package.json

index.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 会从入口文件开始,自动发现所有依赖:

你不需要告诉它”这个项目用了什么”,它自己会分析出来。

多线程处理#

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#

生产构建时自动移除未使用的代码:

math.js
export function add(a, b) {
return a + b
}
export function subtract(a, b) {
return a - b
}
// index.js - 只用了 add
import { 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 文件:

Terminal window
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-reactSVG 转 React 组件
@parcel/transformer-vueVue SFC 支持
@parcel/transformer-mdxMDX 文件支持
parcel-transformer-graphqlGraphQL 文件支持

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 项目:

Terminal window
mkdir react-parcel-app
cd react-parcel-app
npm init -y
npm install react react-dom
npm install parcel --save-dev

src/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:

Terminal window
npm install vue
npm 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 的缓存默认开启,但你可以调整缓存行为:

Terminal window
# 清除缓存
rm -rf .parcel-cache
# 禁用缓存(调试用)
parcel build --no-cache

排除大型依赖#

通过配置排除不需要打包的依赖:

{
"targets": {
"default": {
"externals": {
"react": "React",
"react-dom": "ReactDOM"
}
}
}
}

然后在 HTML 中用 CDN 引入。

Scope Hoisting#

生产构建默认开启,会把多个模块合并到同一个作用域,减少函数调用开销:

Terminal window
# 默认开启
parcel build
# 禁用(调试用)
parcel build --no-scope-hoist

压缩配置#

Parcel 默认用 SWC 压缩 JavaScript,用 cssnano 压缩 CSS。可以自定义:

{
"extends": "@parcel/config-default",
"optimizers": {
"*.js": ["@parcel/optimizer-terser"]
}
}

常见问题#

1. 端口冲突#

Terminal window
# 指定端口
parcel --port 8080
# 或者
parcel -p 8080

2. 热更新不工作#

检查是否有语法错误。Parcel 的 HMR 在遇到错误时可能会回退到刷新整个页面。

也可以试试清除缓存:

Terminal window
rm -rf .parcel-cache
npm run dev

3. 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 分析:

Terminal window
npm install parcel-reporter-bundle-analyzer --save-dev

.parcelrc

{
"extends": "@parcel/config-default",
"reporters": ["...", "parcel-reporter-bundle-analyzer"]
}

与其他工具对比#

对比项ParcelWebpackViteesbuild
配置复杂度极简复杂简单简单
冷启动中等极快
热更新中等
插件生态一般丰富丰富发展中
零配置程度最高中等
适用场景中小型项目所有项目现代项目库、简单项目

什么时候用 Parcel#

Parcel 适合这些场景:

不太适合的场景:

参考资料#