webpack开发环境和生产环境的深入理解

(编辑:jimmy 日期: 2024/11/19 浏览:2)

以前自己写一小项目时,webpack的配置基本就是一套配置,没有考虑生产环境和开发环境的区分,最近在做一个复杂的商城项目接触到了webpack的高级配置,经过两天的研究,写出了一份目前来说比叫满意的配置,在这里分享一下。

如何区分开发环境和生产环境?

众所周知,webpack时基于node.js平台运行的,要区分开发环境和生产环境还要存,node入手。我们启动webpack时,都需要输入一些命令,npm run 、yarn start之类的,所以我们就从命令行入手,告诉webpack,当前是什么环境。

package.json

{
  "name": "webpac-demo",
  "version": "1.0.0",
  "description": "webpack练习",
  "main": "index.js",
  "scripts": {
    //配置开发环境参数。注意:真实开发中 package.json 文件中不能有注释
    "dev": "webpack --env=development",
    //配置生产环境参数
    "dist": "webpack --env=production",
    "start": "webpack-dev-server --env=development"
  },
  "dependencies": {
    "font-awesome": "^4.7.0",
    "react": "^16.2.0",
    "react-dom": "^16.2.0"
  },
  "devDependencies":{
  ...
  }
}

这样配置,当我们在命令行输入 npm run dev 和 npm run dist 时,就会附带一些参数到命令行中,有了参数,我们该如何拿到呢?那就要用到 node 的一个命令行参数解析引擎了。

minimist

minimist轻量级的命令行参数解析引擎

// test.js
var args = require('minimist')(process.argv.slice(2));
console.log(args.hello);

$ node test.js --env=production
// production
$ node test.js --env=development
// development
$ node test.js --env
// true 注意:不是空字符串而是true

minimist会把参数解析成一个JSON对象:{key:value},有了这个JSON对象,我们就可以知道,当前的命令是要执行开发打包,还是生产打包了。

// webpack.config.js

const webpack = require('webpack');
//当前项目的绝对路劲
const path = require('path');

// 命令行参数解析引擎
const argv = require('minimist')(process.argv.slice(2));

let env = null;

switch (argv.env) {
  case 'production':
    //生产环境配置文件名 
    env = 'webpack.config.prod';
    break;
  default:
    //开发环境配置文件名 
    env = 'webpack.config.dev';

}

console.log(`当前是 ${argv.env} 打包`);

// 这时候,我们就可以加载相应的wabpack配置了。
module.exports = require( path.resolve( '加载的配置文件路劲',env ) );

webpack开发环境配置和生产环境配置

开发环境配置

在开发环境下,我们首先考虑的是方便开发,方便代码调试,不需要考虑代码合并和css样式分离这些。

这里主要说三个 :1.css模块化;2.模块热替换功能;3.source-map(代码映射)

// 开发环境打包配置

const path = require('path');
const webpack = require('webpack');
const base = require('./webpack.config.base')
const dfPath = require('./path')
// webpack配置合并工具
const merge =require('webpack-merge')


const RS = (...arg)=>path.resolve( __dirname , ...arg )

// 合并方式配置
let strategyMerge = merge.strategy({
  entry: 'prepend'
});

let config = {
  entry: {
    app: path.resolve(dfPath.root,'src/app.js')
  },

  output: {
    path: dfPath.dist,
    filename: '[name].bundle.js',
    publicPath: '/',
    chunkFilename: '[name].sepChunk.js'
  },
  module:{
    rules: [
      {
        test: /\.js$/,
        use:['babel-loader'],
        exclude: [
          dfPath.node_modules
        ]
      },
      {
        test:/\.css$/,
        use:[
          'style-loader',
          {
            loader:'css-loader',
            options:{
              // css模块化,方便多人开发
              module:true,
              // 定义模块化css后的类名(默认为hash值,可读性差)path:路劲; name:文件名; local:本地定义的className
              localIdentName: '[path][name]__[local]--[hash:base64:5]'
            },
          }
        ],
        // 排除的文件,遇到这些文件不会用当前 loader 处理,也就不会模块化
        exclude:[
          RS('./src/common'),         
          RS('node_modules')
        ]
      },
      {
        test:/\.css$/,
        use:['style-loader','css-loader'],
        include:[
          RS('./src/common'),         
          RS('node_modules')
        ]
        
      },      
      
      {
        test: /\.(png|jpg|jpeg|gif)$/,
        use: ['url-loader"htmlcode">
// 生产环境配置
const webpack = require('webpack');
const base = require('./webpack.config.base')
const path = require('path');
const dfPath = require('./path');
const merge = require('webpack-merge');

// 压缩工具
const ClosureCompilerPlugin = require('webpack-closure-compiler');
// css单独打包插件
const extractTextWebpackPlugin = require('extract-text-webpack-plugin');

const extractCSS = new extractTextWebpackPlugin('assets/css/[name]_[contenthash:6].css');

// weback合并配置
let strategyMerge = merge.strategy({
  entry: 'replace',
  output: 'replace',
  module:{
    rules: 'replace'
  }
});

let config ={

  entry: {
    // 公共模块拆分,这些代码会单独打包,一般我们会把引用的框架文件拆分出来,方便浏览器缓存,节省资源。
    vender:['react'],
    app: path.resolve(dfPath.root,'src/app.js')
  },
  
  output: {
    path: dfPath.dist,
    filename: 'assets/js/[name]_[chunkhash].bundle.js',
    publicPath: '/',
    chunkFilename: 'assets/js/[name].sepChunk.js',
    hashDigestLength: 6
  },

  module:{
    rules: [
      {
        test: /\.js$/,
        use:['babel-loader'],
        exclude: [
          dfPath.node_modules
        ]
      },
      /* 开启 css单独打包 和 css模块化的配置 */ 
      {
        test: /\.css$/,
        use: extractCSS.extract({
          use: [
            {
              loader: 'css-loader',
              options:{
                modules: true
              }              
            }
          ]
        })
      },     

      {
        test: /\.(png|jpg|jpeg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options:{
              limit:8192,
              name: '[name]_[hash].[ext]',
              outputPath: 'assets/img/'
            }
          }
        ],
      },
      
      {
        test: /\.(mp4|ogg|svg|ico)$/,
        use: [
          {
            loader: 'file-loader',
            options:{
              name: '[name]_[hash].[ext]',
              outputPath: 'assets/media/'
            }
          }
        ]
      },
      {
        test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
        use: [

          {
            loader: 'url-loader',
            options:{
              limit:10000,
              name: '[name]_[hash].[ext]',
              outputPath: 'assets/font/',
              mimetype: 'application/font-woff'
            }
          }
        ]
      },
      {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'url-loader',
            options:{
              limit:10000,
              name: '[name]_[hash].[ext]',
              outputPath: 'assets/font/',
              mimetype: 'application/octet-stream'
            }
          }
        ]
      },
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'file-loader',
            options:{
              name: '[name]_[hash].[ext]',
              outputPath: 'assets/font/',
            }
          }
        ]
      },
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'url-loader',
            options:{
              limit:10000,
              name: '[name]_[hash].[ext]',
              outputPath: 'assets/font/',
              mimetype: 'image/svg+xml'
            }
          }
        ]
      },

    ]
  },

  plugins:[
    extractCSS,
    
    // 设置 process.env(生产环境) 环境变量的快捷方式。
    new webpack.EnvironmentPlugin({
      NODE_ENV: 'production'
    })    
    ,new ClosureCompilerPlugin()
  ],

  devtool: 'source-map'
};

module.exports = strategyMerge(base,config);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

一句话新闻
一文看懂荣耀MagicBook Pro 16
荣耀猎人回归!七大亮点看懂不只是轻薄本,更是游戏本的MagicBook Pro 16.
人们对于笔记本电脑有一个固有印象:要么轻薄但性能一般,要么性能强劲但笨重臃肿。然而,今年荣耀新推出的MagicBook Pro 16刷新了人们的认知——发布会上,荣耀宣布猎人游戏本正式回归,称其继承了荣耀 HUNTER 基因,并自信地为其打出“轻薄本,更是游戏本”的口号。
众所周知,寻求轻薄本的用户普遍更看重便携性、外观造型、静谧性和打字办公等用机体验,而寻求游戏本的用户则普遍更看重硬件配置、性能释放等硬核指标。把两个看似难以相干的产品融合到一起,我们不禁对它产生了强烈的好奇:作为代表荣耀猎人游戏本的跨界新物种,它究竟做了哪些平衡以兼顾不同人群的各类需求呢?