webpack基础篇(七):拆分开发环境和生产环境配置

我们手工的调整 mode 选项,可以实现生产环境开发环境的切换,但很多配置在生产环境和开发环境中存在不一致的情况,比如开发环境没有必要设置缓存,生产环境还需要设置公共路径等等。

拆分开发环境和生产环境,让打包更灵活。


1. 公共路径 publicPath

publicPath 配置选项在各种场景中都非常有用。你可以通过它来指定应用程序中所有资源的基础路径。

publicPath在构建目标 targets(默认为web)设置为 webweb-workeroutput.publicPath 默认为 `‘auto’

实质上,发送到 output.path 目录的每个文件,都将从 output.publicPath 位置引用。这也包括(通过 代码分离 创建的)子 chunk 和作为依赖图一部分的所有其他资源(例如 image, font 等)。

在前面,我们没有设置 output.publicPath,即为auto时,我们在dist/index.html 看到资源的引用路径为:
在这里插入图片描述

当我们想要将打包出的静态资源放置在cdn服务器上,这个dist/index.html该如何加载资源呢?

webpack.config.js

module.exports = {
    
    
  // ...
  output: {
    
    
    publicPath: 'http://localhost:9000/',
    filename: 'js/[name].[contenthash].js',
    path: path.resolve(__dirname, './dist'),
  },
}

执行webpack可以看到,dist/index.html加载的资源路径已经变化。实际上所有的静态资源路径都发生了变化
在这里插入图片描述


2. 环境变量

想要消除 webpack.config.js开发环境生产环境 之间的差异,你可能需要环境变量(environment variable)。

webpack 命令行 环境配置--env 参数,可以允许你传入任意数量的环境变量。而在 webpack.config.js 中可以访问到这些环境变量。例如,--env production--env goal=local

npx webpack --env goal=local --env production --progress

Tip

如果设置 env 变量,却没有赋值,--env production 默认表示将 env.production 设置为 true。还有许多其他可以使用的语法。更多详细信息,请查看 webpack CLI 文档。

对于我们的 webpack 配置,有一个必须要修改之处。通常,module.exports 指向配置对象。要使用 env 变量,你必须将 module.exports 转换成一个函数:

const path = require('path');

module.exports = (env) => {
    
    
  console.log('env', env);

  return {
    
    
    entry: './src/index.js',
    output: {
    
    
      filename: 'bundle.js',
      path: path.resolve(__dirname, './dist'),
    },
  };
};

在这里插入图片描述

3. 拆分配置文件

目前,生产环境和开发环境使用的是一个配置文件,我们需要将这两个文件单独放到不同的配置文件中。如 webpack.config.dev.js

(开发环境配置)和 webpack.config.prod.js(生产环境配置)。在项目根目录下创建一个配置文件夹 config 来存放他们。

config/webpack.config.dev.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const toml = require('toml');
const yaml = require('yamljs');
const json5 = require('json5');

module.exports = {
    
    
  entry: {
    
    
    index: './src/index.js',
    another: './src/another_module.js',
  },

  // 出口配置
  output: {
    
    
    filename: 'js/[name].js', // 出口文件名
    // 绝对路径
    path: path.resolve(__dirname, '../dist'), // path.resolve(__dirname) 表示获取当前文件的物理路径,即绝对路径
    clean: true, // 在生成文件之前清空 output 目录
    assetModuleFilename: 'images/[hash][ext][query]'
  },

  devtool: 'inline-source-map',

  mode: 'development', // 打包模式
  watch: true,

  devServer: {
    
    
    static: '../dist',
    port: 9000,
    open: true
  },

  module: {
    
    
    rules: [
      {
    
    
        test: /\.png$/,
        type: 'asset/resource',
        generator: {
    
    
          filename: 'images/[hash][ext][query]'
        }
      },
      {
    
    
        test: /\.svg$/,
        type: 'asset/inline'
      },
      {
    
    
        test: /\.txt$/,
        type: 'asset/source'
      },
      {
    
    
        test: /\.jpeg$/,
        type: 'asset',
        parser: {
    
    
          dataUrlCondition: {
    
    
            maxSize: 4 * 1024 // 4kb
          }
        }
      },
      {
    
    
        test: /\.(css|less)$/i,
        use: [
          // compiles Less to CSS
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader',
        ],
      },
      {
    
    
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
      {
    
    
        test: /\.(csv|tsv)$/i,
        use: 'csv-loader'
      },
      {
    
    
        test: /\.xml$/i,
        use: ['xml-loader'],
      },
      {
    
    
        test: /\.toml$/i,
        type: 'json',
        parser: {
    
    
          parse: toml.parse,
        },
      },
      {
    
    
        test: /\.yaml$/i,
        type: 'json',
        parser: {
    
    
          parse: yaml.parse,
        },
      },
      {
    
    
        test: /\.json5$/i,
        type: 'json',
        parser: {
    
    
          parse: json5.parse,
        },
      },
      {
    
    
        test: /\.js$/,
        exclude: /(node_modules)/, // 排除编译 node_modules
        use: {
    
    
          loader: 'babel-loader',
          options: {
    
    
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-transform-runtime'],
          }
        }
      }
    ]
  },

  optimization: {
    
    
    minimize: true, // 开发环境下启用 CSS 优化
    splitChunks: {
    
    
      cacheGroups: {
    
    
        vendor: {
    
    
          // 第三方模块一般处于node_modules中,所以这里缓存 node_modules 中第三方的模块
          test: /[\\/]node_modules[\\/]/, // 缓存node_modules中的文件夹名和文件(文件目录前后可能会有斜线/)
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },

  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './index.html', // 模板
      filename: 'index.html', // 文件名称
      inject: 'body' // 定义生成的script所在的位置
    }),

    // 提取css
    new MiniCssExtractPlugin({
    
    
      filename: 'styles/[hash].css'
    })
  ],
}

config/webpack.config.prod.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin')
const toml = require('toml');
const yaml = require('yamljs');
const json5 = require('json5');

module.exports = {
    
    
  entry: {
    
    
    index: './src/index.js',
    another: './src/another_module.js',
  },

  // 出口配置
  output: {
    
    
    filename: 'js/[name].[contenthash].js', // 出口文件名
    // 绝对路径
    path: path.resolve(__dirname, '../dist'), // path.resolve(__dirname) 表示获取当前文件的物理路径,即绝对路径
    clean: true, // 在生成文件之前清空 output 目录
    assetModuleFilename: 'images/[hash][ext][query]'
  },

  mode: 'production', // 打包模式

  module: {
    
    
    rules: [
      {
    
    
        test: /\.png$/,
        type: 'asset/resource',
        generator: {
    
    
          filename: 'images/[hash][ext][query]'
        }
      },
      {
    
    
        test: /\.svg$/,
        type: 'asset/inline'
      },
      {
    
    
        test: /\.txt$/,
        type: 'asset/source'
      },
      {
    
    
        test: /\.jpeg$/,
        type: 'asset',
        parser: {
    
    
          dataUrlCondition: {
    
    
            maxSize: 4 * 1024 // 4kb
          }
        }
      },
      {
    
    
        test: /\.(css|less)$/i,
        use: [
          // 提取css
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader',
        ],
      },
      {
    
    
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
      {
    
    
        test: /\.(csv|tsv)$/i,
        use: 'csv-loader'
      },
      {
    
    
        test: /\.xml$/i,
        use: ['xml-loader'],
      },
      {
    
    
        test: /\.toml$/i,
        type: 'json',
        parser: {
    
    
          parse: toml.parse,
        },
      },
      {
    
    
        test: /\.yaml$/i,
        type: 'json',
        parser: {
    
    
          parse: yaml.parse,
        },
      },
      {
    
    
        test: /\.json5$/i,
        type: 'json',
        parser: {
    
    
          parse: json5.parse,
        },
      },
      {
    
    
        test: /\.js$/,
        exclude: /(node_modules)/, // 排除编译 node_modules
        use: {
    
    
          loader: 'babel-loader',
          options: {
    
    
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-transform-runtime'],
          }
        }
      }
    ]
  },

  optimization: {
    
    
    minimizer: [
      new CssMinimizerPlugin(), // css 压缩
      new TerserPlugin() // js 压缩
    ],
    splitChunks: {
    
    
      cacheGroups: {
    
    
        vendor: {
    
    
          // 第三方模块一般处于node_modules中,所以这里缓存 node_modules 中第三方的模块
          test: /[\\/]node_modules[\\/]/, // 缓存node_modules中的文件夹名和文件(文件目录前后可能会有斜线/)
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },

  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './index.html', // 模板
      filename: 'index.html', // 文件名称
      inject: 'body' // 定义生成的script所在的位置
    }),

    // css 提取
    new MiniCssExtractPlugin({
    
    
      filename: 'styles/[hash].css'
    })
  ],
  //关闭 webpack 的性能提示
  performance: {
    
    
    hints: false
  }
}

拆分成两个配置文件后,分别运行这两个文件:

webpack --config ./config/webpack.config.dev.js查看开发环境打包结果

webpack serve --config ./config/webpack.config.dev.js 查看开发环境打项目打包加载结果

webpack --config ./config/webpack.config.prod.js查看开发环境打包结果

webpack serve --config ./config/webpack.config.prod.js 查看开发环境打项目打包加载结果


4. npm脚本

配置 npm 脚本来简化命令行的输入

package.json

{
    
    
  "scripts": {
    
    
    "start": "webpack serve -c ./config/webpack.config.dev.js",
    "build": "webpack -c ./config/webpack.config.prod.js"
  },
}

开发环境运行脚本npm run start

生产环境运行脚本npm run build


5. 提取公共配置

我们发现这两个配置文件里存在大量的重复代码,可以手动的将这些重复的代码单独提取到一个文件里,

创建 config/webpack.config.common.js,配置公共的内容:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const toml = require('toml');
const yaml = require('yamljs');
const json5 = require('json5');

module.exports = {
    
    
  entry: {
    
    
    index: './src/index.js',
    another: './src/another_module.js',
  },

  // 出口配置
  output: {
    
    
    // 绝对路径
    path: path.resolve(__dirname, '../dist'), // path.resolve(__dirname) 表示获取当前文件的物理路径,即绝对路径
    clean: true, // 在生成文件之前清空 output 目录
    assetModuleFilename: 'images/[hash][ext][query]'
  },

  module: {
    
    
    rules: [
      {
    
    
        test: /\.png$/,
        type: 'asset/resource',
        generator: {
    
    
          filename: 'images/[hash][ext][query]'
        }
      },
      {
    
    
        test: /\.svg$/,
        type: 'asset/inline'
      },
      {
    
    
        test: /\.txt$/,
        type: 'asset/source'
      },
      {
    
    
        test: /\.jpeg$/,
        type: 'asset',
        parser: {
    
    
          dataUrlCondition: {
    
    
            maxSize: 4 * 1024 // 4kb
          }
        }
      },
      {
    
    
        test: /\.(css|less)$/i,
        use: [
          // compiles Less to CSS
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader',
        ],
      },
      {
    
    
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
      {
    
    
        test: /\.(csv|tsv)$/i,
        use: 'csv-loader'
      },
      {
    
    
        test: /\.xml$/i,
        use: ['xml-loader'],
      },
      {
    
    
        test: /\.toml$/i,
        type: 'json',
        parser: {
    
    
          parse: toml.parse,
        },
      },
      {
    
    
        test: /\.yaml$/i,
        type: 'json',
        parser: {
    
    
          parse: yaml.parse,
        },
      },
      {
    
    
        test: /\.json5$/i,
        type: 'json',
        parser: {
    
    
          parse: json5.parse,
        },
      },
      {
    
    
        test: /\.js$/,
        exclude: /(node_modules)/, // 排除编译 node_modules
        use: {
    
    
          loader: 'babel-loader',
          options: {
    
    
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-transform-runtime'],
          }
        }
      }
    ]
  },

  optimization: {
    
    
    splitChunks: {
    
    
      cacheGroups: {
    
    
        vendor: {
    
    
          // 第三方模块一般处于node_modules中,所以这里缓存 node_modules 中第三方的模块
          test: /[\\/]node_modules[\\/]/, // 缓存node_modules中的文件夹名和文件(文件目录前后可能会有斜线/)
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },

  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './index.html', // 模板
      filename: 'index.html', // 文件名称
      inject: 'body' // 定义生成的script所在的位置
    }),

    new MiniCssExtractPlugin({
    
    
      filename: 'styles/[hash].css'
    })
  ],
}

修改 config/webpack.confjg.dev.js

module.exports = {
    
    
  // 出口配置
  output: {
    
    
    filename: 'js/[name].js', // 出口文件名
  },

  devtool: 'inline-source-map',

  mode: 'development', // 打包模式
  watch: true,

  devServer: {
    
    
    static: '../dist',
    port: 9000,
    open: true
  },

  optimization: {
    
    
    minimize: true, // 开发环境下启用 CSS 优化
  },
}

修改 config/webpack.confjg.prod.js

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
    
    
  // 出口配置
  output: {
    
    
    filename: 'js/[name].[contenthash].js', // 出口文件名
  },

  mode: 'production', // 打包模式

  optimization: {
    
    
    minimizer: [
      new CssMinimizerPlugin(), // css 压缩
      new TerserPlugin() // js 压缩
    ],
  },

  //关闭 webpack 的性能提示
  performance: {
    
    
    hints: false
  }
}

6. 合并配置文件

配置文件拆分好后,新的问题来了,如何保证配置合并没用问题呢?webpack merge 这个工具可以完美解决这个问题。

npm install webpack-merge -D

config 目录下创建 webpack.config.js ,合并代码

const {
    
     merge } = require('webpack-merge')
const commonConfig = require('./webpack.config.common.js')
const productionConfig = require('./webpack.config.prod.js')
const developmentConfig = require('./webpack.config.dev.js')
module.exports = (env) =>{
    
    
  switch (true) 
    case env.development:
      return merge(commonConfig, developmentConfig)
    case env.production:
      return merge(commonConfig, productionConfig)
    default:
      throw new Error('No matching configuration was found!');
  }
}

修改package.json npm脚本

{
    
    
  "scripts": {
    
    
    "start": "webpack serve -c ./config/webpack.config.js --env development",
    "build": "webpack -c ./config/webpack.config.js --env prodoction"
  },
}

分别执行 npm run startnpm run build 可以看到编译、打包、加载都是成功的!



源码地址:https://gitee.com/yanhuakang/webpack-test

如果有用,就点个赞吧(\*^▽^\*)

猜你喜欢

转载自blog.csdn.net/qq_41887214/article/details/121670227