跳到主要内容

构建工具

现代前端开发离不开构建工具,它们负责代码转换、打包、优化等任务,是前端工程化的核心。

Webpack

基础配置

javascript
1// webpack.config.js
2const path = require('path');
3const HtmlWebpackPlugin = require('html-webpack-plugin');
4const MiniCssExtractPlugin = require('mini-css-extract-plugin');
5
6module.exports = {
7 entry: './src/index.js',
8 output: {
9 path: path.resolve(__dirname, 'dist'),
10 filename: '[name].[contenthash].js',
11 clean: true,
12 publicPath: '/'
13 },
14 module: {
15 rules: [
16 {
17 test: /\.js$/,
18 exclude: /node_modules/,
19 use: {
20 loader: 'babel-loader',
21 options: {
22 presets: ['@babel/preset-env', '@babel/preset-react']
23 }
24 }
25 },
26 {
27 test: /\.css$/,
28 use: [
29 process.env.NODE_ENV === 'production'
30 ? MiniCssExtractPlugin.loader
31 : 'style-loader',
32 'css-loader',
33 'postcss-loader'
34 ]
35 },
36 {
37 test: /\.(png|svg|jpg|jpeg|gif)$/i,
38 type: 'asset/resource'
39 },
40 {
41 test: /\.(woff|woff2|eot|ttf|otf)$/i,
42 type: 'asset/resource'
43 }
44 ]
45 },
46 plugins: [
47 new HtmlWebpackPlugin({
48 template: './public/index.html'
49 }),
50 new MiniCssExtractPlugin({
51 filename: '[name].[contenthash].css'
52 })
53 ],
54 optimization: {
55 splitChunks: {
56 chunks: 'all',
57 cacheGroups: {
58 vendor: {
59 test: /[\\/]node_modules[\\/]/,
60 name: 'vendors',
61 chunks: 'all'
62 }
63 }
64 }
65 },
66 devServer: {
67 contentBase: path.join(__dirname, 'dist'),
68 compress: true,
69 port: 3000,
70 hot: true,
71 historyApiFallback: true
72 },
73 mode: process.env.NODE_ENV || 'development'
74};

高级配置

javascript
1// webpack.prod.js - 生产环境配置
2const { merge } = require('webpack-merge');
3const common = require('./webpack.common.js');
4const TerserPlugin = require('terser-webpack-plugin');
5const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
6const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
7
8module.exports = merge(common, {
9 mode: 'production',
10 devtool: 'source-map',
11 optimization: {
12 minimize: true,
13 minimizer: [
14 new TerserPlugin({
15 terserOptions: {
16 compress: {
17 drop_console: true,
18 drop_debugger: true
19 }
20 }
21 }),
22 new CssMinimizerPlugin()
23 ],
24 splitChunks: {
25 chunks: 'all',
26 cacheGroups: {
27 default: {
28 minChunks: 2,
29 priority: -20,
30 reuseExistingChunk: true
31 },
32 vendor: {
33 test: /[\\/]node_modules[\\/]/,
34 name: 'vendors',
35 priority: -10,
36 chunks: 'all'
37 },
38 react: {
39 test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
40 name: 'react',
41 chunks: 'all'
42 }
43 }
44 }
45 },
46 plugins: [
47 new BundleAnalyzerPlugin({
48 analyzerMode: 'static',
49 openAnalyzer: false
50 })
51 ]
52});

Vite

Vite是新一代前端构建工具,以其快速的开发服务器和优化的构建性能而闻名。

基础配置

javascript
1// vite.config.js
2import { defineConfig } from 'vite';
3import react from '@vitejs/plugin-react';
4import { resolve } from 'path';
5
6export default defineConfig({
7 plugins: [react()],
8 resolve: {
9 alias: {
10 '@': resolve(__dirname, 'src'),
11 '@components': resolve(__dirname, 'src/components'),
12 '@utils': resolve(__dirname, 'src/utils')
13 }
14 },
15 css: {
16 preprocessorOptions: {
17 scss: {
18 additionalData: `@import "@/styles/variables.scss";`
19 }
20 },
21 modules: {
22 localsConvention: 'camelCase'
23 }
24 },
25 build: {
26 outDir: 'dist',
27 sourcemap: true,
28 rollupOptions: {
29 output: {
30 manualChunks: {
31 vendor: ['react', 'react-dom'],
32 router: ['react-router-dom']
33 }
34 }
35 }
36 },
37 server: {
38 port: 3000,
39 open: true,
40 proxy: {
41 '/api': {
42 target: 'http://localhost:8080',
43 changeOrigin: true,
44 rewrite: (path) => path.replace(/^\/api/, '')
45 }
46 }
47 }
48});

环境配置

javascript
1// vite.config.ts - TypeScript配置
2import { defineConfig, loadEnv } from 'vite';
3import react from '@vitejs/plugin-react';
4import { resolve } from 'path';
5
6export default defineConfig(({ command, mode }) => {
7 const env = loadEnv(mode, process.cwd(), '');
8
9 return {
10 plugins: [
11 react({
12 jsxImportSource: '@emotion/react',
13 babel: {
14 plugins: ['@emotion/babel-plugin']
15 }
16 })
17 ],
18 define: {
19 __APP_ENV__: JSON.stringify(env.APP_ENV)
20 },
21 resolve: {
22 alias: {
23 '@': resolve(__dirname, 'src')
24 }
25 },
26 build: {
27 target: 'es2015',
28 minify: 'terser',
29 terserOptions: {
30 compress: {
31 drop_console: command === 'build',
32 drop_debugger: command === 'build'
33 }
34 }
35 }
36 };
37});

Rollup

Rollup专注于ES模块打包,特别适合库的构建。

基础配置

javascript
1// rollup.config.js
2import resolve from '@rollup/plugin-node-resolve';
3import commonjs from '@rollup/plugin-commonjs';
4import babel from '@rollup/plugin-babel';
5import { terser } from 'rollup-plugin-terser';
6import peerDepsExternal from 'rollup-plugin-peer-deps-external';
7
8export default {
9 input: 'src/index.js',
10 output: [
11 {
12 file: 'dist/index.cjs.js',
13 format: 'cjs',
14 sourcemap: true
15 },
16 {
17 file: 'dist/index.esm.js',
18 format: 'esm',
19 sourcemap: true
20 },
21 {
22 file: 'dist/index.umd.js',
23 format: 'umd',
24 name: 'MyLibrary',
25 sourcemap: true
26 }
27 ],
28 plugins: [
29 peerDepsExternal(),
30 resolve({
31 browser: true
32 }),
33 commonjs(),
34 babel({
35 babelHelpers: 'bundled',
36 exclude: 'node_modules/**',
37 presets: ['@babel/preset-env', '@babel/preset-react']
38 }),
39 terser()
40 ],
41 external: ['react', 'react-dom']
42};

esbuild

esbuild是用Go编写的极快的JavaScript打包器和压缩器。

基础使用

javascript
1// build.js
2const esbuild = require('esbuild');
3
4esbuild.build({
5 entryPoints: ['src/index.js'],
6 bundle: true,
7 outfile: 'dist/bundle.js',
8 minify: true,
9 sourcemap: true,
10 target: ['es2015'],
11 loader: {
12 '.png': 'dataurl',
13 '.svg': 'text'
14 },
15 define: {
16 'process.env.NODE_ENV': '"production"'
17 }
18}).catch(() => process.exit(1));

开发服务器

javascript
1// dev-server.js
2const esbuild = require('esbuild');
3
4esbuild.serve({
5 servedir: 'public',
6 port: 3000
7}, {
8 entryPoints: ['src/index.js'],
9 bundle: true,
10 outdir: 'public/dist',
11 loader: {
12 '.js': 'jsx'
13 }
14}).then(server => {
15 console.log(`Server running at http://localhost:${server.port}`);
16});

Parcel

Parcel是零配置的Web应用打包器。

基础使用

json
1{
2 "name": "my-app",
3 "scripts": {
4 "start": "parcel src/index.html",
5 "build": "parcel build src/index.html"
6 },
7 "devDependencies": {
8 "parcel": "^2.0.0"
9 }
10}

配置文件

json
1// .parcelrc
2{
3 "extends": "@parcel/config-default",
4 "transformers": {
5 "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
6 },
7 "resolvers": ["@parcel/resolver-default"],
8 "bundler": "@parcel/bundler-default",
9 "namers": ["@parcel/namer-default"],
10 "runtimes": ["@parcel/runtime-js", "@parcel/runtime-browser-hmr"],
11 "optimizers": {
12 "*.js": ["@parcel/optimizer-terser"],
13 "*.css": ["@parcel/optimizer-cssnano"]
14 },
15 "packagers": {
16 "*.html": "@parcel/packager-html",
17 "*.js": "@parcel/packager-js",
18 "*.css": "@parcel/packager-css"
19 },
20 "compressors": {
21 "*.{html,css,js,svg,map}": ["@parcel/compressor-gzip"]
22 }
23}

构建优化策略

1. 代码分割

javascript
1// 动态导入实现代码分割
2const LazyComponent = React.lazy(() => import('./LazyComponent'));
3
4// Webpack魔法注释
5const LazyComponent = React.lazy(() =>
6 import(/* webpackChunkName: "lazy-component" */ './LazyComponent')
7);
8
9// 路由级别分割
10const routes = [
11 {
12 path: '/',
13 component: React.lazy(() => import('./pages/Home'))
14 },
15 {
16 path: '/about',
17 component: React.lazy(() => import('./pages/About'))
18 }
19];

2. Tree Shaking

javascript
1// 确保使用ES模块
2import { debounce } from 'lodash-es'; // ✅ 支持tree shaking
3// import _ from 'lodash'; // ❌ 不支持tree shaking
4
5// package.json中标记副作用
6{
7 "sideEffects": false, // 或者 ["*.css", "*.scss"]
8}
9
10// 使用具名导出
11export const utils = {
12 debounce,
13 throttle
14};
15
16// 而不是默认导出
17export default {
18 debounce,
19 throttle
20};

3. 缓存策略

javascript
1// Webpack缓存配置
2module.exports = {
3 cache: {
4 type: 'filesystem',
5 buildDependencies: {
6 config: [__filename]
7 }
8 },
9 output: {
10 filename: '[name].[contenthash].js',
11 chunkFilename: '[name].[contenthash].chunk.js'
12 }
13};

4. 性能监控

javascript
1// webpack-bundle-analyzer
2const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
3
4module.exports = {
5 plugins: [
6 new BundleAnalyzerPlugin({
7 analyzerMode: 'static',
8 openAnalyzer: false,
9 reportFilename: 'bundle-report.html'
10 })
11 ]
12};
13
14// 性能预算
15module.exports = {
16 performance: {
17 maxAssetSize: 250000,
18 maxEntrypointSize: 250000,
19 hints: 'warning'
20 }
21};

构建工具对比

特性WebpackViteRollupesbuildParcel
配置复杂度极低
构建速度极快
开发服务器支持极快需插件支持支持
代码分割强大支持支持支持支持
插件生态丰富快速增长丰富有限中等
学习成本极低
适用场景大型项目现代项目库开发快速构建快速原型

最佳实践

1. 选择合适的工具

  • 大型复杂项目:Webpack + 详细配置
  • 现代React/Vue项目:Vite
  • 库开发:Rollup
  • 快速原型:Parcel
  • 性能要求极高:esbuild

2. 优化构建性能

javascript
1// 并行处理
2const TerserPlugin = require('terser-webpack-plugin');
3
4module.exports = {
5 optimization: {
6 minimizer: [
7 new TerserPlugin({
8 parallel: true, // 启用并行压缩
9 cache: true // 启用缓存
10 })
11 ]
12 }
13};
14
15// 减少解析范围
16module.exports = {
17 resolve: {
18 modules: [path.resolve(__dirname, 'src'), 'node_modules'],
19 extensions: ['.js', '.jsx', '.ts', '.tsx']
20 },
21 module: {
22 rules: [
23 {
24 test: /\.js$/,
25 include: path.resolve(__dirname, 'src'), // 只处理src目录
26 exclude: /node_modules/
27 }
28 ]
29 }
30};

3. 环境区分

javascript
1// 开发环境
2const devConfig = {
3 mode: 'development',
4 devtool: 'eval-source-map',
5 devServer: {
6 hot: true,
7 open: true
8 }
9};
10
11// 生产环境
12const prodConfig = {
13 mode: 'production',
14 devtool: 'source-map',
15 optimization: {
16 minimize: true,
17 splitChunks: {
18 chunks: 'all'
19 }
20 }
21};

通过合理选择和配置构建工具,可以显著提升开发效率和应用性能。

常用Loader

javascript
1// webpack.config.js
2module.exports = {
3 module: {
4 rules: [
5 // JavaScript处理
6 {
7 test: /\.js$/,
8 exclude: /node_modules/,
9 use: 'babel-loader'
10 },
11
12 // TypeScript处理
13 {
14 test: /\.ts$/,
15 use: 'ts-loader'
16 },
17
18 // CSS处理
19 {
20 test: /\.css$/,
21 use: ['style-loader', 'css-loader']
22 },
23
24 // SCSS处理
25 {
26 test: /\.scss$/,
27 use: ['style-loader', 'css-loader', 'sass-loader']
28 },
29
30 // 图片处理
31 {
32 test: /\.(png|jpg|gif|svg)$/,
33 type: 'asset/resource'
34 },
35
36 // 字体处理
37 {
38 test: /\.(woff|woff2|eot|ttf|otf)$/,
39 type: 'asset/resource'
40 }
41 ]
42 }
43};

常用Plugin

javascript
1// webpack.config.js
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3const MiniCssExtractPlugin = require('mini-css-extract-plugin');
4const { CleanWebpackPlugin } = require('clean-webpack-plugin');
5
6module.exports = {
7 plugins: [
8 // 生成HTML文件
9 new HtmlWebpackPlugin({
10 template: './src/index.html',
11 filename: 'index.html'
12 }),
13
14 // 提取CSS到单独文件
15 new MiniCssExtractPlugin({
16 filename: '[name].[contenthash].css'
17 }),
18
19 // 清理构建目录
20 new CleanWebpackPlugin()
21 ]
22};

开发服务器

javascript
1// webpack.config.js
2module.exports = {
3 devServer: {
4 static: './dist',
5 hot: true,
6 port: 3000,
7 open: true,
8 historyApiFallback: true
9 }
10};

Vite

基础配置

javascript
1// vite.config.js
2import { defineConfig } from 'vite';
3import react from '@vitejs/plugin-react';
4
5export default defineConfig({
6 plugins: [react()],
7 root: './src',
8 build: {
9 outDir: '../dist',
10 rollupOptions: {
11 input: {
12 main: './src/index.html'
13 }
14 }
15 },
16 server: {
17 port: 3000,
18 open: true
19 }
20});

插件配置

javascript
1// vite.config.js
2import { defineConfig } from 'vite';
3import react from '@vitejs/plugin-react';
4import legacy from '@vitejs/plugin-legacy';
5
6export default defineConfig({
7 plugins: [
8 react(),
9 legacy({
10 targets: ['defaults', 'not IE 11']
11 })
12 ],
13 css: {
14 preprocessorOptions: {
15 scss: {
16 additionalData: `@import "@/styles/variables.scss";`
17 }
18 }
19 }
20});

环境变量

javascript
1// vite.config.js
2export default defineConfig(({ command, mode }) => {
3 const env = loadEnv(mode, process.cwd(), '');
4
5 return {
6 define: {
7 __APP_VERSION__: JSON.stringify(process.env.npm_package_version)
8 },
9 server: {
10 port: env.VITE_PORT || 3000
11 }
12 };
13});

Rollup

基础配置

javascript
1// rollup.config.js
2import resolve from '@rollup/plugin-node-resolve';
3import commonjs from '@rollup/plugin-commonjs';
4import babel from '@rollup/plugin-babel';
5
6export default {
7 input: 'src/index.js',
8 output: [
9 {
10 file: 'dist/bundle.js',
11 format: 'cjs'
12 },
13 {
14 file: 'dist/bundle.esm.js',
15 format: 'es'
16 }
17 ],
18 plugins: [
19 resolve(),
20 commonjs(),
21 babel({
22 exclude: 'node_modules/**'
23 })
24 ],
25 external: ['lodash']
26};

库打包配置

javascript
1// rollup.config.js
2import resolve from '@rollup/plugin-node-resolve';
3import commonjs from '@rollup/plugin-commonjs';
4import typescript from '@rollup/plugin-typescript';
5
6export default {
7 input: 'src/index.ts',
8 output: [
9 {
10 file: 'dist/index.js',
11 format: 'cjs',
12 sourcemap: true
13 },
14 {
15 file: 'dist/index.esm.js',
16 format: 'es',
17 sourcemap: true
18 }
19 ],
20 plugins: [
21 resolve(),
22 commonjs(),
23 typescript({
24 tsconfig: './tsconfig.json'
25 })
26 ],
27 external: ['react', 'react-dom']
28};

Parcel

零配置使用

bash
1# 安装Parcel
2npm install --save-dev parcel
3
4# 开发模式
5npx parcel src/index.html
6
7# 构建
8npx parcel build src/index.html

配置文件

json
1// .parcelrc
2{
3 "extends": "@parcel/config-default",
4 "transformers": {
5 "*.{jpg,jpeg,png,gif,svg}": ["@parcel/transformer-image"]
6 }
7}

工具对比

性能对比

工具启动速度热更新构建速度配置复杂度
Webpack中等
Vite中等
Rollup中等中等
Parcel中等

适用场景

  • Webpack:大型项目,需要复杂配置
  • Vite:现代项目,快速开发体验
  • Rollup:库开发,ES模块输出
  • Parcel:简单项目,零配置需求

优化策略

代码分割

javascript
1// webpack.config.js
2module.exports = {
3 optimization: {
4 splitChunks: {
5 chunks: 'all',
6 cacheGroups: {
7 vendor: {
8 test: /[\\/]node_modules[\\/]/,
9 name: 'vendors',
10 chunks: 'all'
11 }
12 }
13 }
14 }
15};

缓存优化

javascript
1// webpack.config.js
2module.exports = {
3 output: {
4 filename: '[name].[contenthash].js',
5 chunkFilename: '[name].[contenthash].js'
6 },
7 optimization: {
8 moduleIds: 'deterministic',
9 chunkIds: 'deterministic'
10 }
11};

压缩优化

javascript
1// webpack.config.js
2const TerserPlugin = require('terser-webpack-plugin');
3
4module.exports = {
5 optimization: {
6 minimize: true,
7 minimizer: [
8 new TerserPlugin({
9 terserOptions: {
10 compress: {
11 drop_console: true
12 }
13 }
14 })
15 ]
16 }
17};

现代构建工具

ESBuild

javascript
1// esbuild.config.js
2const esbuild = require('esbuild');
3
4esbuild.build({
5 entryPoints: ['src/index.js'],
6 bundle: true,
7 outfile: 'dist/bundle.js',
8 minify: true,
9 sourcemap: true,
10 target: ['es2020']
11}).catch(() => process.exit(1));

SWC

javascript
1// .swcrc
2{
3 "jsc": {
4 "parser": {
5 "syntax": "ecmascript",
6 "jsx": true
7 },
8 "transform": {
9 "react": {
10 "runtime": "automatic"
11 }
12 }
13 }
14}

最佳实践

环境配置

javascript
1// webpack.config.js
2const isProduction = process.env.NODE_ENV === 'production';
3
4module.exports = {
5 mode: isProduction ? 'production' : 'development',
6 devtool: isProduction ? 'source-map' : 'eval-cheap-module-source-map',
7 optimization: {
8 minimize: isProduction
9 }
10};

多环境配置

javascript
1// webpack.config.js
2const configs = {
3 development: {
4 mode: 'development',
5 devtool: 'eval-source-map'
6 },
7 production: {
8 mode: 'production',
9 devtool: 'source-map'
10 }
11};
12
13module.exports = (env, argv) => {
14 const mode = argv.mode || 'development';
15 return {
16 ...configs[mode],
17 entry: './src/index.js',
18 output: {
19 path: path.resolve(__dirname, 'dist'),
20 filename: 'bundle.js'
21 }
22 };
23};

评论