vue 项目 webpack 打包配置

2024-12-24
webpack
23

简介

了解完单个webpack的配置之后,我们来总结一下,一个 vue 项目需要的webpack配置的通用方案

本文从零开始,搭建一个 vue 项目,再给出对应的webpack配置。项目使用 vue3 + ts + less。

项目所有配置使用 webpack5,webpack 4 不分配置不支持。

核心思路

要完成一个项目的打包,可以需要做哪些事情

image

逐步拆解

入口、出口

入口配置使用 entry,出口配置 使用 output,这一块比较简单

{
  entry: path.resolve(__dirname, '../src/main.ts'),
  // 输出文件配置
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, '../dist'),
    publicPath: '/',
  },
}

更详细的配置信息可以参考:webpack 入口和出口-webfem

代码转译 - loader

loader 的详细解释可见:https://webfem.com/post/webpack-loader

转译思路

vue

vue 代码的转译需要用到 vue-loader 和 vueLoaderPlugin 插件两者共同参与

css

css 和 less 在开发环境只需要 到 style-loader 即可,生产环境需要加上 MiniCssExtractPlugin.loader 做压缩

图片资源

图片资源的转译推荐使用 asset/resource,具体可以参考 https://webfem.com/post/webpack-assets

完整 loader 配置

相关解释已添加注释

{
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
      {
        test: /\.ts$/, // 添加 .ts 文件的处理规则
        loader: 'ts-loader', // 使用 ts-loader 编译 TypeScript
        exclude: /node_modules/,
        options: {
          appendTsSuffixTo: [/\.vue$/],
          transpileOnly: true, // 添加此行以提高编译速度
          compilerOptions: {
            // 添加编译器选项
            moduleResolution: 'node',
          },
        },
      },
      {
        test: /\.(js|tsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env', '@babel/preset-typescript'],
              plugins: ['@vue/babel-plugin-jsx'],
            },
          },
        ],
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader'],
      },
      {
        test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|eot|ttf|otf)$/,
        type: 'asset/resource',
        generator: {
          filename: 'assets/[name].[hash][ext]',
          publicPath:
            process.env.NODE_ENV === 'development'
              ? '/'
              : `${process.env.VUE_ASSETS_URL}/`,
        },
      },
    ],
  },
  plugins: [
    new VueLoaderPlugin(), // 必须添加 VueLoaderPlugin

  ],
}

代码分包

代码拆包是个根据项目情况,灵活拆分的工作。这里先不过多介绍,我们可以简单来一个配置:

optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendors: {
        test: /[\/]node_modules[\/]/,
        name: 'vendors',
        chunks: 'all',
      },
  },
},

将公共代码打包到一起

代码压缩

代码压缩详细介绍可以参考:https://webfem.com/post/webpack-assets-mini

压缩思路

相关配置

{
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../index.html'),
      inject: true,
    }), // html 拆分与压缩
    new VueLoaderPlugin(), // 必须添加 VueLoaderPlugin
  ],
  optimization: {
    minimizer: [
      `...`, // 使用默认的 JavaScript 压缩工具
      new CssMinimizerPlugin({
        filename: '[name].[contenthash].css',
      }), // 添加 CSS 压缩
    ],
  },
}

调试服务

调试服务需要用到 webpack devServer,它会自动启动一个 http 服务,支持热更新,方便调试,详细配置见

// 配置开发服务器
  devServer: {
    client: {
      overlay: false, // 禁用错误覆盖层,个人喜好这个可选,
    },
    static: {
      directory: path.join(__dirname, '../dist'), // 静态文件目录
    },
    hot: true, // 启用热模块替换
    open: true, // 启动后自动打开浏览器
    historyApiFallback: true, // 支持前端路由
    port: 8080
  },

路径别名

添加路径别名,可以让代码少写一些 '../../' ,同时webpack也会缓存路径,增加效率

{
  resolve: {
    alias: {
      '@': path.resolve(__dirname, '../src'),
    },
    extensions: ['.js', '.vue', '.json', '.ts', '.tsx'],
  },
}

这里我们简单配置一个 @ 指向根目录就行

总结

思维导图

整体思路

完整配置

dev

/* eslint-disable @typescript-eslint/no-require-imports */
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const webpack = require('webpack');
// const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  // 开发模式:对构建速度进行优化,自动生成 source-map
  mode: 'development',
  // 入口文件
  entry: path.resolve(__dirname, '../src/main.ts'),
  // 输出文件配置
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, '../dist'),
  },
  // 配置开发服务器
  devServer: {
    client: {
      overlay: false, // 禁用错误覆盖层
    },
    static: {
      directory: path.join(__dirname, '../dist'), // 静态文件目录
    },
    hot: true, // 启用热模块替换
    open: true, // 启动后自动打开浏览器
    historyApiFallback: true, // 支持前端路由
    port: 8080,
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, '../src'),
    },
    extensions: ['.js', '.vue', '.json', '.ts', '.tsx'],
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
      {
        test: /\.ts$/, // 添加 .ts 文件的处理规则
        loader: 'ts-loader', // 使用 ts-loader 编译 TypeScript
        exclude: /node_modules/,
        options: {
          appendTsSuffixTo: [/\.vue$/],
          transpileOnly: true, // 添加此行以提高编译速度
          compilerOptions: {
            // 添加编译器选项
            moduleResolution: 'node',
          },
        },
      },
      {
        test: /\.(js|tsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env', '@babel/preset-typescript'],
              plugins: ['@vue/babel-plugin-jsx'],
            },
          },
        ],
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader'],
      },
      {
        test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|eot|ttf|otf)$/,
        type: 'asset/resource',
        generator: {
          filename: 'assets/[name].[hash][ext]',
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../index.html'),
      inject: true,
    }),
    // process.env.ANALYZE &&
    //   new BundleAnalyzerPlugin({
    //     analyzerPort: process.env.ANALYZER_PORT || 8889,
    //     analyzerHost: 'localhost',
    //     openAnalyzer: true,
    //   }),
    new VueLoaderPlugin(), // 必须添加 VueLoaderPlugin
  ],
  // 配置 source-map,用于开发时调试
  devtool: process.env.NODE_ENV === 'development' ? 'eval-source-map' : false,
};

prod

/* eslint-disable @typescript-eslint/no-require-imports */
const { merge } = require('webpack-merge');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const common = require('./webpack.dev.config');

module.exports = merge(common, {
  mode: 'production',
  output: {
    filename: 'bundle.[contenthash].js',
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
  },
});

原文地址:https://webfem.com/post/webpack-vue,转载请注明出处