Webpack 4区分开发环境和生产环境

阅读 (778)
webpack提供 mode 配置选项,告知 webpack 使用相应环境的内置优化。

官方对mode介绍

先看一下官方对mode介绍:

https://webpack.docschina.org/concepts/mode/#%E7%94%A8%E6%B3%95

只需在配置对象中提供 mode 选项:

module.exports = {
  mode: 'production'
};

或者从 CLI 参数中传递: 

webpack --mode=production
选项 描述
选项
development
描述
会将 DefinePluginprocess.env.NODE_ENV 的值设置为 development。启用 NamedChunksPluginNamedModulesPlugin
选项
production
描述
会将 DefinePluginprocess.env.NODE_ENV 的值设置为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPluginTerserPlugin
选项
none
描述
退出任何默认优化选项

但我在package.json中配置了webpack --mode=production,在npm run build时,貌似并没有获取到

如在webpack.base.conf.js中打印process.env.NODE_ENV,取不到值。

先看一下我的目的结构,比较简单,主要就是用webpack区分开发和生产环境,webpack.base.conf.js中写一些公用的配置

因为开发模式不需要压缩 css,将不同的压缩 rule 分别写在 webpack.prod.conf.js 和 webpack.dev.conf.js 文件里:

区分环境分开配置

webpack.dev.conf.js

rules: [
    {
        test: /\.(sa|sc|c)ss$/,
        use: [
            'style-loader', //开发模式不使用插件
            'css-loader',
            'sass-loader',
        ]
    }
]

上面的配置也可以写在webpack.base.conf.js , 然后只需针对prod写不同的配置覆盖base中的配置即可

webpack.prod.conf.js 

rules: [
  {
    test: /\.css|scss$/,
    use: [
      {
        loader: MiniCssExtractPlugin.loader,
        options: {
          // 设置css中资源链接的路径
          // publicPath 默认使用 webpackOptions.output中的publicPath, 如果图片和样式在同一层目录,可以使用默认值
          // 由于我上面设置的图片打包路径是 /dist/images, 而css是分块存储到 /dist/css目录中,其中引用的图片路径应为“../images/xxx.[ext]”,固这里设置为'../'
          publicPath: '../'
        }
      },
      'css-loader',
      'sass-loader',
    ]
  }
]

合并代码到base

但如果想直接在webpack.base.conf.js中直接区分这块代码,比如通常我们写个三目运算进行判断就行了,我想这么写:如果是dev,就加载style-loader, 否则就使用MiniCssExtractPlugin对样式进行抽离

rules: [
    {
        test: /\.css|scss$/,
        use: [
          devMode ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              // 设置css中资源链接的路径
              // publicPath 默认使用 webpackOptions.output中的publicPath, 如果图片和样式在同一层目录,可以使用默认值
              // 由于我上面设置的图片打包路径是 /dist/images, 而css是分块存储到 /dist/css目录中,其中引用的图片路径应为“../images/xxx.[ext]”,固这里设置为'../'
              publicPath: '../'
            }
          },
          'css-loader',
          'sass-loader',
        ]
      }
    ]
]

这时我就发现,拿不到process.env.NODE_ENV值,此时就要想想办法怎么拿到process.env.NODE_ENV

下面说一下在使用中遇到的问题和解决方案,看一下最终完整的webpack配置

webpack.base.conf.js:

'use strict'
const path = require('path');
const webpack = require('webpack');
const config = require('../config')

const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== 'production'
console.log('环境', process.env.NODE_ENV)
module.exports = {
  // 入口起点
  entry: {
    app: './src/main.js',
  },
  // 输出
  // path是webpack所有文件的输出的路径
  // 比如:output输出的js,url-loader解析的图片,“path”仅仅告诉Webpack结果存储在哪里,
  // HtmlWebpackPlugin生成的html文件,都会存放在以path为基础的目录下
  // path即所有输出文件的目标路径;打包后文件在硬盘中的存储位置。
  // 如下path配置是将所有打包后生成的文件及目录放到上层路径的“dist”目录
  // path.resolve(__dirname, '../dist') 是磁盘绝对路径
  // publicPath:输出解析文件的目录,指定资源文件引用的目录 ,打包后浏览器访问服务时的 url 路径中通用的一部分。
  // publicPath项则被许多Webpack的插件用于在生产模式下更新内嵌到css、html文件里的url值。
  // publicPath设置'./'
  // 例:在文件中引用了某张图片,在url-loader设置的图片存储位置是images/[name].[hash].[ext]
  // 那最终在生产环境中<img src="./images/xxx.[ext]">的形式

  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: "[name].js"
  },
  // 解析
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.json'],
    alias: {
      '@': path.join(__dirname, '..', 'src')
    }
  },
  // loader
  module: {
    rules: [
      {
        test: /\.js|jsx$/,
        exclude: /(node_modules)/,// 屏蔽不需要处理的文件(文件夹)
        loader: 'babel-loader'
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 5000, // 最小图片尺寸5kb,5kb以下直接用base64图片
          name: 'images/[name].[hash].[ext]' // 加上.[hash],防止各目录中出现相同文件名图片,如果不使用[hash],在汇总到images目录中后,图片会被替换
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 5000,
          name: 'medias/[name].[hash].[ext]'
        }
      },
      {
        test: /\.(woff|woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 5000,
          name: 'fonts/[name].[hash].[ext]'
        }
      },
      {
        test: /\.css|scss$/,
        use: [
          devMode ? 'style-loader' : {
            loader: MiniCssExtractPlugin.loader,
            options: {
              // 设置css中资源链接的路径
              // publicPath 默认使用 webpackOptions.output中的publicPath, 如果图片和样式在同一层目录,可以使用默认值
              // 由于我上面设置的图片打包路径是 /dist/images, 而css是分块存储到 /dist/css目录中,其中引用的图片路径应为“../images/xxx.[ext]”,固这里设置为'../'
              publicPath: '../'
            }
          },
          'css-loader',
          'sass-loader',
        ]
      }
    ]
  },
  // 目标运行环境
  target: "web",
  // 插件
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: 'body'
    }),
    new webpack.DefinePlugin({
      'APPID': JSON.stringify(config.APPID),
      'COURSE': JSON.stringify(config.COURSE)
    })
  ]
};

webpack.dev.conf.js

'use strict'
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const config = require('../config/dev.env')
const path = require('path');
const webpack = require('webpack');
module.exports = merge(baseWebpackConfig, {
  // 模式
  mode: "development",
  // 调试工具
  devtool: 'inline-source-map',
  // 开发服务器
  devServer: {
    contentBase: false,// 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录
    historyApiFallback: true,// 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
    compress: true,// 启用gzip压缩
    inline: true,// 设置为true,当源文件改变时会自动刷新页面
    hot: true,// 模块热更新,取决于HotModuleReplacementPlugin
    host: '127.0.0.1',// 设置默认监听域名,如果省略,默认为“localhost”
    port: 3000// 设置默认监听端口,如果省略,默认为“8080”
  },
  // 插件
  plugins: [
    // 热更新相关
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    // 这里的设置是给src中用的,在webpack.base.conf如要获取,需在package.json配置cross-env NODE_ENV=development
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(config.mode),
      'API_ROOT': JSON.stringify(config.API_ROOT)
    }),
  ],
  optimization: {
    nodeEnv: config.mode,
  }
});

webpack.prod.conf.js

'use strict'
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const config = require('../config/prod.env')
const path = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
/**
 * 提取css到单独文件
 * npm install --save-dev mini-css-extract-plugin
 */
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
/**
 * css 压缩 会清除css中注释
 * npm install --save-dev optimize-css-assets-webpack-plugin
 */
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = merge(baseWebpackConfig, {
  // 模式
  mode: "production",
  // 调试工具
  devtool: '#source-map',
  // 输出
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: "js/[name].[chunkhash].js",
    publicPath: './'
  },
  // 插件
  plugins: [
    // 这里的设置是给src中用的,在webpack.base.conf如要获取,需在package.json配置cross-env NODE_ENV=production
    new webpack.DefinePlugin({
      'process.env': JSON.stringify(config.mode),
      'API_ROOT': JSON.stringify(config.API_ROOT)
    }),
    // 打包时先删除dist目录,这里与output: path配置的目录同名即可,即:打包到哪个目录,就先删除那个目录,重新生成文件
    new CleanWebpackPlugin(['dist'], {
      root: path.resolve(__dirname, '../'),
    }),
    new webpack.HashedModuleIdsPlugin(),
    // 配置plugin
    new MiniCssExtractPlugin({
      // 导出 css
      filename: "css/[name].[hash:5].css",
      chunkFilename: "css/[id].[hash:5].css"
    }),
    new UglifyJSPlugin({
      uglifyOptions: {
        compress: {
          drop_debugger: false,
          drop_console: true
        }
      }
    }),
    new OptimizeCSSAssetsPlugin({
      // 默认是全部的CSS都压缩,该字段可以指定某些要处理的文件
      assetNameRegExp: /\.(sa|sc|c)ss$/g,
      // 指定一个优化css的处理器,默认cssnano
      cssProcessor: require('cssnano'),

      cssProcessorPluginOptions: {
        preset: [  'default', {
            discardComments: { removeAll: true}, //对注释的处理
            normalizeUnicode: false // 建议false,否则在使用unicode-range的时候会产生乱码
        }]
      },
      canPrint: true  // 是否打印编译过程中的日志
    }),
    // html打包
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: 'body',
      // inject 有4个选项,按最新标准,一般将js文件置于body底部
      // true 默认值,script标签位于html文件的 body 底部
      // body script标签位于html文件的 body 底部
      // head script标签位于html文件的 head中
      // false 不插入生成的js文件,这个几乎不会用到的

      // 压缩html
      minify: {
        // 移除注释
        removeComments: true,
        // 不要留下任何空格
        collapseWhitespace: true,
        // 当值匹配默认值时删除属性
        removeRedundantAttributes: true,
        // 使用短的doctype替代doctype
        useShortDoctype: true,
        // 移除空属性
        removeEmptyAttributes: true,
        // 从style和link标签中删除type="text/css"
        removeStyleLinkTypeAttributes: true,
        // 保留单例元素的末尾斜杠。
        keepClosingSlash: true,
        // 在脚本元素和事件属性中缩小JavaScript(使用UglifyJS)
        minifyJS: true,
        // 缩小CSS样式元素和样式属性
        minifyCSS: true,
        // 在各种属性中缩小url
        minifyURLs: true
      }
    }),
    // 复制内容到指定目录,一般针对一些静态资源
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../public'),
        to: path.resolve(__dirname, '../dist'),
        ignore: ['.*']
      }
    ])
  ],
  // 代码分离相关
  optimization: {
    nodeEnv: config.mode,
    minimizer: [],
    runtimeChunk: {
      name: 'manifest'
    },
    splitChunks: {
      minSize: 30000,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      name: false,
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          chunks: 'initial',
        }
      }
    }
  }
});

在 npm run dev 和 npm run build 时, 在base中都获取不到 process.env.NODE_ENV 的值

安装使用cross-env获取env

安装:

npm install cross-env --save-dev

在 NPM 脚本scripts中加上:

cross-env NODE_ENV=production 和 cross-env NODE_ENV=development

package.json

{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": { // cross-env NODE_ENV=development、 cross-env NODE_ENV=production需要用到 cross-env插件
    "dev": "cross-env NODE_ENV=development webpack-dev-server --hot --inline --progress --colors --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "build": "cross-env NODE_ENV=production webpack --progress --colors --config build/webpack.prod.conf.js"
  },
  "author": "",
  "homepage": "",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-plugin-import": "^1.11.0",
    "babel-preset-latest": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "clean-webpack-plugin": "^1.0.0",
    "cross-env": "^7.0.0", // 这个插件在上面的,scripts中配置cross-env NODE_ENV=development 或 production,在webpack配置文件中就能拿到NODE_ENV的值了
    "css-loader": "^1.0.1",
    "file-loader": "^2.0.0",
    "html-webpack-plugin": "^3.2.0",
    "mini-css-extract-plugin": "^0.9.0",
    "node-sass": "^4.10.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "uglifyjs-webpack-plugin": "^2.0.1",
    "url-loader": "^1.1.2",
    "webpack": "^4.26.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10",
    "webpack-merge": "^4.1.4"
  },
  "dependencies": {
    "antd": "^3.26.11",
    "antd-mobile": "^2.3.1",
    "axios": "^0.19.2",
    "clipboard": "^2.0.4",
    "qs": "^6.9.1",
    "react": "^16.6.3",
    "react-dom": "^16.6.3",
    "react-redux": "^6.0.0",
    "react-router-dom": "^4.3.1",
    "redux": "^4.0.5"
  }
}

测试

再分别运行开发和生产环境 npm run build,和 npm run dev

查看控制台,就能看到正确的输出了

更新于:2020-03-13 11:16:22
返回顶部