构建工具

构建工具是现代前端开发的基石,它们将源代码转换为可部署的生产代码,处理模块打包、代码转换、资源优化等任务。

构建工具概述

为什么需要构建工具

任务 说明 工具示例
代码转换 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

Webpack 快速入门

安装

# 初始化项目
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
  }
}

package.json 脚本

{
  "scripts": {
    "dev": "webpack serve --mode development",
    "build": "webpack --mode production"
  }
}

Vite 快速入门

创建项目

# 创建项目
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: []
})

Vite 特性

// 支持 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>

Rollup 快速入门

安装

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

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'
  })
})

API 使用

// 转换代码
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 快速入门

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 转译器

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

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-dev-server

// 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 开发服务器

// 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')

Tree Shaking

// 确保使用 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)—— 让构建更高效