构建工具是现代前端开发的基石,它们将源代码转换为可部署的生产代码,处理模块打包、代码转换、资源优化等任务。
| 任务 | 说明 | 工具示例 |
|---|---|---|
| 代码转换 | ES6+ 转 ES5、TS 转 JS | Babel、tsc |
| 模块打包 | 合并模块、处理依赖 | Webpack、Rollup |
| 代码压缩 | 减小文件体积 | Terser、UglifyJS |
| 资源优化 | 图片压缩、CSS 优化 | imagemin、cssnano |
| 开发服务器 | 热更新、代理 | webpack-dev-server |
| 代码检查 | ESLint、Stylelint | eslint-loader |
| 工具 | 特点 | 适用场景 |
|---|---|---|
| Webpack | 功能全面、生态丰富 | 大型应用 |
| Vite | 开发快、配置简单 | 现代项目 |
| Rollup | 打包体积小 | 库开发 |
| Parcel | 零配置 | 小型项目 |
| esbuild | 极速构建 | 追求速度 |
| Turbopack | 增量编译 | Next.js |
# 初始化项目
npm init -y
# 安装 webpack
npm install -D webpack webpack-cli
// webpack.config.js
const path = require('path')
module.exports = {
// 入口
entry: './src/index.js',
// 输出
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
clean: true
},
// 模式
mode: 'development',
// 模块规则
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
},
// 插件
plugins: [],
// 开发服务器
devServer: {
static: './dist',
hot: true,
port: 3000
}
}
{
"scripts": {
"dev": "webpack serve --mode development",
"build": "webpack --mode production"
}
}
# 创建项目
npm create vite@latest dongba-project
# 选择模板
# vanilla, vue, react, preact, lit, svelte
# 安装依赖
cd dongba-project
npm install
# 启动开发服务器
npm run dev
// vite.config.js
import { defineConfig } from 'vite'
import path from 'path'
export default defineConfig({
// 根目录
root: './src',
// 公共基础路径
base: '/',
// 模式
mode: 'development',
// 开发服务器
server: {
port: 3000,
open: true,
cors: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
},
// 构建配置
build: {
outDir: '../dist',
sourcemap: true,
minify: 'esbuild',
rollupOptions: {
output: {
manualChunks: {
vendor: ['lodash', 'axios']
}
}
}
},
// 路径别名
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
// 插件
plugins: []
})
// 支持 ES 模块
import { createApp } from 'vue'
import _ from 'lodash'
import './style.css'
// 支持动态导入
const module = await import('./module.js')
// 支持 CSS 预处理器
import './style.scss'
import './style.less'
import './style.styl'
// 支持 CSS Modules
import styles from './module.css'
console.log(styles.container)
// 支持 TypeScript
import { User } from './types'
// 支持 JSX
const App = () => <div>东巴文</div>
npm install -D rollup
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import babel from '@rollup/plugin-babel'
import terser from '@rollup/plugin-terser'
export default {
// 入口
input: 'src/index.js',
// 输出
output: [
{
file: 'dist/bundle.cjs.js',
format: 'cjs',
sourcemap: true
},
{
file: 'dist/bundle.esm.js',
format: 'esm',
sourcemap: true
},
{
file: 'dist/bundle.umd.js',
format: 'umd',
name: 'DongbaUtils',
sourcemap: true
}
],
// 插件
plugins: [
resolve(),
commonjs(),
babel({ babelHelpers: 'bundled' }),
terser()
],
// 外部依赖
external: ['lodash']
}
| 格式 | 说明 | 使用场景 |
|---|---|---|
esm |
ES 模块 | 现代浏览器、打包工具 |
cjs |
CommonJS | Node.js |
umd |
通用模块定义 | 浏览器 Script 标签 |
iife |
立即执行函数 | 浏览器 Script 标签 |
amd |
AMD 模块 | RequireJS |
system |
SystemJS 模块 | SystemJS |
esbuild 是一个极速的 JavaScript 打包工具。
npm install -D esbuild
const esbuild = require('esbuild')
// 构建
esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
minify: true,
sourcemap: true,
target: ['es2020'],
format: 'esm',
platform: 'browser'
}).then(() => {
console.log('构建完成')
})
// 开发模式
esbuild.context({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js'
}).then(ctx => {
ctx.watch()
ctx.serve({
port: 3000,
servedir: 'dist'
})
})
// 转换代码
const result = await esbuild.transform(
'const x = () => {}',
{ minify: true }
)
console.log(result.code)
// 构建同步版本
esbuild.buildSync({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js'
})
Parcel 是零配置的构建工具。
npm install -D parcel
{
"scripts": {
"dev": "parcel src/index.html",
"build": "parcel build src/index.html"
}
}
<!-- src/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>东巴文</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="app"></div>
<script type="module" src="./index.js"></script>
</body>
</html>
// src/index.js
import { greet } from './utils'
import './style.css'
greet('东巴文')
// .parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.js": ["@parcel/transformer-js"]
}
}
Babel 是 JavaScript 编译器,将现代语法转换为兼容代码。
npm install -D @babel/core @babel/cli @babel/preset-env
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: '18',
browsers: ['> 1%', 'last 2 versions']
},
useBuiltIns: 'usage',
corejs: 3
}
]
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-transform-runtime'
]
}
# 编译文件
npx babel src --out-dir lib
# 编译并监听
npx babel src --out-dir lib --watch
# 编译并生成 sourcemap
npx babel src --out-dir lib --source-maps
| 预设/插件 | 说明 |
|---|---|
@babel/preset-env |
根据目标环境自动确定插件 |
@babel/preset-react |
React JSX 转换 |
@babel/preset-typescript |
TypeScript 转换 |
@babel/plugin-transform-runtime |
复用辅助函数 |
@babel/plugin-proposal-decorators |
装饰器支持 |
PostCSS 是 CSS 后处理器。
npm install -D postcss postcss-cli
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default'
}),
require('postcss-preset-env')({
stage: 0
})
]
}
# 编译
npx postcss src/style.css --output dist/style.css
# 监听
npx postcss src/style.css --output dist/style.css --watch
| 插件 | 说明 |
|---|---|
autoprefixer |
自动添加浏览器前缀 |
cssnano |
CSS 压缩 |
postcss-preset-env |
使用现代 CSS 特性 |
postcss-import |
处理 @import |
postcss-nested |
嵌套语法 |
tailwindcss |
Tailwind CSS |
// webpack.config.js
module.exports = {
devServer: {
static: './dist',
hot: true,
port: 3000,
open: true,
compress: true,
historyApiFallback: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
}
// vite.config.js
export default {
server: {
port: 3000,
host: true,
https: false,
open: true,
cors: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
}
// Webpack
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
}
// Vite
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
utils: ['lodash', 'dayjs']
}
}
}
}
}
// 动态导入
const module = await import('./module.js')
// 确保使用 ES 模块语法
// package.json
{
"sideEffects": false
}
// 或指定有副作用的文件
{
"sideEffects": ["*.css", "*.vue"]
}
// Webpack
module.exports = {
output: {
filename: '[name].[contenthash].js',
assetModuleFilename: '[name].[hash][ext]'
},
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
}
}
// Vite
export default {
build: {
rollupOptions: {
output: {
entryFileNames: '[name].[hash].js',
chunkFileNames: '[name].[hash].js',
assetFileNames: '[name].[hash][extname]'
}
}
}
}
了解构建工具后,你可以继续学习:
东巴文(db-w.cn)—— 让构建更高效