publicPath
配置选项在各种场景中都非常有用。你可以通过它来指定应用程序中所有资源的基础路径。
-
基于环境设置
在开发环境中,我们通常有一个
assets/
文件夹,它与索引页面位于同一级别。这没太大问题,但是,如果我们将所有静态资源托管至 CDN,然后想在生产环境中使用呢?想要解决这个问题,可以直接使用一个 environment variable(环境变量)。假设我们有一个变量
ASSET_PATH
:import webpack from 'webpack'; // 尝试使用环境变量,否则使用根路径 const ASSET_PATH = process.env.ASSET_PATH || '/'; export default { output: { publicPath: ASSET_PATH, }, plugins: [ // 这可以帮助我们在代码中安全地使用环境变量 new webpack.DefinePlugin({ 'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH), }), ], };
11-multiple-env/webpack.config.js
//... import webpack from 'webpack'; // 尝试使用环境变量,否则使用根路径 const ASSET_PATH = process.env.ASSET_PATH || '/'; export default { output: { //... publicPath: ASSET_PATH, }, plugins: [ // 这可以帮助我们在代码中安全地使用环境变量 new webpack.DefinePlugin({ 'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH), }), //... ], };
-
有可能你事先不知道 publicPath 是什么,webpack 会自动根据
import.meta.url
、document.currentScript
、script.src
或者self.location
变量设置 publicPath。你需要做的是将output.publicPath
设为'auto'
:module.exports = { output: { publicPath: 'auto', }, };
请注意在某些情况下不支持
document.currentScript
,例如:IE 浏览器,你不得不引入一个 polyfill,例如currentScript Polyfill
。
想要消除 webpack.config.js
在 开发环境 和 生产环境 之间的差异,你可能需要环境变量(environment variable)。
webpack 命令行 环境配置 的 --env
参数,可以允许你传入任意数量的环境变量。而在 webpack.config.js
中可以访问到这些环境变量。例如,--env production
或 --env goal=local
。
npx webpack --env goal=local --env production --progress
对于我们的 webpack 配置,有一个必须要修改之处。通常,module.exports
指向配置对象。要使用 env
变量,你必须将 module.exports
转换成一个函数:
11-multiple-env/webpack.config.js
//... module.exports = (env) => { return { //... // 根据命令行参数 env 来设置不同环境的 mode mode: env.production ? 'production' : 'development', //... } }
目前,生产环境和开发环境使用的是一个配置文件,我们需要将这两个文件单独放到不同的配置文件中。如webpack.config.dev.js
(开发环境配置)和 webpack.config.prod.js
(生产环境配置)。在项目根目录下创建一个配置文件夹 config
来存放他们。
webpack.config.dev.js
配置如下:
11-multiple-env/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('yaml') const json5 = require('json5') module.exports = { entry: { index: './src/index.js', another: './src/another-module.js' }, output: { filename: 'scripts/[name].js', path: path.resolve(__dirname, './dist'), clean: true, assetModuleFilename: 'images/[contenthash][ext]' }, mode: 'development', devtool: 'inline-source-map', plugins: [ new HtmlWebpackPlugin({ template: './index.html', filename: 'app.html', inject: 'body' }), new MiniCssExtractPlugin({ filename: 'styles/[contenthash].css' }) ], devServer: { static: './dist' }, module: { rules: [ { test: /\.png$/, type: 'asset/resource', generator: { filename: 'images/[contenthash][ext]' } }, { test: /\.svg$/, type: 'asset/inline' }, { test: /\.txt$/, type: 'asset/source' }, { test: /\.jpg$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 4 * 1024 * 1024 } } }, { test: /\.(css|less)$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, type: 'asset/resource' }, { test: /\.(csv|tsv)$/, use: 'csv-loader' }, { test: /\.xml$/, use: 'xml-loader' }, { test: /\.toml$/, type: 'json', parser: { parse: toml.parse } }, { test: /\.yaml$/, type: 'json', parser: { parse: yaml.parse } }, { test: /\.json5$/, type: 'json', parser: { parse: json5.parse } }, { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], plugins: [ [ '@babel/plugin-transform-runtime' ] ] } } } ] }, optimization: { splitChunks: { cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all' } } } } }
webpack.config.prod.js
配置如下:
11-multiple-env/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 toml = require('toml') const yaml = require('yaml') const json5 = require('json5') module.exports = { entry: { index: './src/index.js', another: './src/another-module.js' }, output: { filename: 'scripts/[name].[contenthash].js', // 打包的dist文件夹要放到上一层目录 path: path.resolve(__dirname, '../dist'), clean: true, assetModuleFilename: 'images/[contenthash][ext]', publicPath: 'http://localhost:8080/' }, mode: 'production', plugins: [ new HtmlWebpackPlugin({ template: './index.html', filename: 'app.html', inject: 'body' }), new MiniCssExtractPlugin({ filename: 'styles/[contenthash].css' }) ], module: { rules: [ { test: /\.png$/, type: 'asset/resource', generator: { filename: 'images/[contenthash][ext]' } }, { test: /\.svg$/, type: 'asset/inline' }, { test: /\.txt$/, type: 'asset/source' }, { test: /\.jpg$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 4 * 1024 * 1024 } } }, { test: /\.(css|less)$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, type: 'asset/resource' }, { test: /\.(csv|tsv)$/, use: 'csv-loader' }, { test: /\.xml$/, use: 'xml-loader' }, { test: /\.toml$/, type: 'json', parser: { parse: toml.parse } }, { test: /\.yaml$/, type: 'json', parser: { parse: yaml.parse } }, { test: /\.json5$/, type: 'json', parser: { parse: json5.parse } }, { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], plugins: [ [ '@babel/plugin-transform-runtime' ] ] } } } ] }, optimization: { minimizer: [ new CssMinimizerPlugin() ], splitChunks: { cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all' } } } }, //关闭 webpack 的性能提示 performance: { hints:false } }
拆分成两个配置文件后,分别运行这两个文件:
开发环境:
[felix] 10-multiple-env $ npx webpack serve -c ./config/webpack.config.dev.js
生产环境:
[felix] 10-multiple-env $ npx webpack -c ./config/webpack.config.prod.js
每次打包或启动服务时,都需要在命令行里输入一长串的命令。我们将父目录的 package.json
、node_modules
与 package-lock.json
拷贝到当前目录下,
配置 npm 脚本来简化命令行的输入,这时可以省略 npx
:
11-multiple-env/package.json
{ "scripts": { "start": "webpack serve -c ./config/webpack.config.dev.js", "build": "webpack -c ./config/webpack.config.prod.js" } }
开发环境运行脚本:
[felix] 10-multiple-env $ npm run start
[felix] 10-multiple-env $ npm run build
这时,我们发现这两个配置文件里存在大量的重复代码,可以手动的将这些重复的代码单独提取到一个文件里,
创建 webpack.config.common.js
,配置公共的内容:
11-multiple-env/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('yaml') const json5 = require('json5') module.exports = { entry: { index: './src/index.js', another: './src/another-module.js' }, output: { // 注意这个dist的路径设置成上一级 path: path.resolve(__dirname, '../dist'), clean: true, assetModuleFilename: 'images/[contenthash][ext]', }, plugins: [ new HtmlWebpackPlugin({ template: './index.html', filename: 'app.html', inject: 'body' }), new MiniCssExtractPlugin({ filename: 'styles/[contenthash].css' }) ], module: { rules: [ { test: /\.png$/, type: 'asset/resource', generator: { filename: 'images/[contenthash][ext]' } }, { test: /\.svg$/, type: 'asset/inline' }, { test: /\.txt$/, type: 'asset/source' }, { test: /\.jpg$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 4 * 1024 } } }, { test: /\.(css|less)$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, type: 'asset/resource' }, { test: /\.(csv|tsv)$/, use: 'csv-loader' }, { test: /\.xml$/, use: 'xml-loader' }, { test: /\.toml$/, type: 'json', parser: { parse: toml.parse } }, { test: /\.yaml$/, type: 'json', parser: { parse: yaml.parse } }, { test: /\.json5$/, type: 'json', parser: { parse: json5.parse } }, { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], plugins: [ [ '@babel/plugin-transform-runtime' ] ] } } } ] }, optimization: { splitChunks: { cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all' } } } }, //关闭 webpack 的性能提示 performance: { hints:false } }
改写 webpack.config.dev.js
:
11-multiple-env/config/webpack.config.dev.js
module.exports = { // 开发环境不需要配置缓存 output: { filename: 'scripts/[name].js', }, // 开发模式 mode: 'development', // 配置 source-map devtool: 'inline-source-map', // 本地服务配置 devServer: { static: './dist' } }
修改 webpack.config.prod.js
:
11-multiple-env/config/webpack.config.prod.js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') module.exports = { // 生产环境需要缓存 output: { filename: 'scripts/[name].[contenthash].js', publicPath: 'http://localhost:8080/' }, // 生产环境模式 mode: 'production', // 生产环境 css 压缩 optimization: { minimizer: [ new CssMinimizerPlugin() ] } }
配置文件拆分好后,新的问题来了,如何保证配置合并没用问题呢?webpack-merge 这个工具可以完美解决这个问题。
安装 webpack-merge
:
[felix] felixcourses $ npm install webpack-merge -D
创建 webpack.config.js
,合并代码:
11-multiple-env/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') 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!'); } }
-- 本篇完 --