webpack生产环境优化及配置总结

一 介绍

之前在开发一个react网站项目, 到正式部署到云服务器时才发现对react及webpack生产环境配置没有什么了解, 下面就总结一下自己配置生产环境的内容.

二 需要配置的内容

1. js文件压缩

首先就是要进行打包后的js文件的压缩, 其中要使用uglifyjs-webpack-plugin, 首先使用npm安装这个包, 然后在配置文件中加入插件配置项:

// js压缩插件
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

然后再在plugins插件部分添加它的使用:

...
plugins: [
    // 压缩Js
    new UglifyJSPlugin({
      sourceMap: true
    })
  ]
...

2. css压缩

css压缩配置相对容易, 只要在css-loader的options中加入压缩的选项为true即可, 即:

...
{
    loader: 'css-loader',
    options: {
        minimize: true
    }
}
...

3. 图片压缩

此处需要配置两个内容: 一是url-loader二是image-webpack-loader. 两个loader联用可以将各类在引入时进行压缩. 配置如下:

...
{
test: /\.(gif|png|jpe?g|svg)$/i,
  use: [
    {
      loader: 'url-loader',
      options: {
        limit: 8192
      }
    },
    {
      loader: 'image-webpack-loader',
      options: {
        bypassOnDebug: true,
        mozjpeg: {
          progressive: true,
          quality: 40
        },
      },
    },
  ]
}
...

image-webpack-loader中的mozjpeg为例, mozjpeg是用来压缩jpeg格式文件的配置, 其中的qulity即是其压缩质量为40%, 其余对不同的图片格式使用了不同的技术去压缩, 需要分别配置.
在配置时有几个注意的点:
1. 经测试, 在js文件及css文件中引用图片要使用相对路径格式, webpack会将引入的文件单独压缩打包到输出文件夹中. 如import product1 from '../img/product/product-img1.png'
2. 在webpack官网配置教程中使用的是file-loaderimage-webpack-loader, 这种配置方式对css中引用的图片无法进行压缩, 所以要使用url-loader才可以对css中如background: url(...)中引用的图片进行压缩.

4. 提取代码公共部分

可以将代码中公共的部分提取处理单独引用, 其中要使用webpack的CommonsChunkPlugin插件, 配置方法是在plugin中加入相应的内容:

...
plugins: [
    // Plugin for code splitting, 抽离出公共部分单独打包
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common' // Specify the common bundle's name.
    })
    ...
  ]
...

在抽离出公共部分后, html中也要相应的加入内容, 如:

<script src="common.bundle.js">script>

5. css单独提取

可以将所有css单独提取为一个文件, 和js等并行进行读取. 其中要使用ExtractTextPlugin, 配置方法分两部分, 一是在loader部分配置, 二是在plugin部分配置.

...
{
 test: /\.(css|less)$/,
 // 此处使用ExtractTextPlugin插件
 use: ExtractTextPlugin.extract({
   fallback: 'style-loader',
   use: [
     {
       loader: 'css-loader',
       options: {
         minimize: true
       }
     },
     {
       loader: 'less-loader'
     }
   ]
 })
}
...

plugins: [
  // Plugin for code splitting, css单独打包
  new ExtractTextPlugin('style.css'),
]
...

此处配置执行打包所有css为style.css这个文件, 然后需要在html中也单独引用这一css.

6. react生产环境

react配置生产模式与process.env.NODE_ENV环境变量关联, 配置方法是使用 webpack 内置的 DefinePlugin.

...
plugins: [
  // react配置webpack生产模式
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify('production')
  })
]
...

7. react与babel

使用react生产环境时, 在babel中也要相应进行配置, 对开发和生产环境分别进行配置:


  "presets": [
    "react",
    "es2015"
  ],
"env": {
   "development": {
     "plugins": [
       [
         "react-transform",
         {
           "transforms": [
             {
               "transform": "react-transform-hmr",
               "imports": [
                 "react"
               ],
               "locals": [
                 "module"
               ]
             }
           ]
         }
       ],
       [
         "syntax-dynamic-import",
         [
           "import-inspector",
           {
             "serverSideRequirePath": true
           }
         ]
       ]
     ]
   },
   "production": {
     "plugins": [
       [
         "syntax-dynamic-import",
         [
           "import-inspector",
           {
             "serverSideRequirePath": true
           }
         ]
       ]
     ]
   }
 }
}

其中的development和production分别配置了开发环境和生产环境中使用的插件. 如此处的区别就在于生产环境中去除了热更新的部分, 都保留了动态import句法的部分.
另外, 在生产环境进行打包时, 需要在命令行中加入BABEL_ENV=production, 告诉babel本次打包是用在生产环境中.

三 webpack配置文件分离

之前在开发时都是直接使用一个webpack.config.js配置文件, 如果要区分生产和开发两种环境, 就要对配置文件进行分离. 参考官方教程, 新建webpack.dev.jswebpack.prod.js.
然后, 为了避免重复, 要将两种环境下相同的内容放在一个公共的配置文件中, 所以新建webpack.common.js用来保存这个部分:

const webpack = require('webpack');
// css代码分割Plugin
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const OUTPUT_PATH = path.resolve(__dirname, 'dist');

module.exports = {
  entry: {
    index: './app/index.js',
  },
  output: {
    // __dirname: 全局变量, 存储的是文件所在的文件目录
    // path.resolve()(node)将一系列路径或路径片段解析为一个绝对路径
    path: OUTPUT_PATH,
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {
        // test: 一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
        test: /(\.js|\.jsx)$/,
        // include/exclude: 手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选)
        exclude: /^node_modules$/,
        use: {
          loader: 'babel-loader'
          // 此处的options: {...} 单独提取到了.babelrc中
        }
      },
      {
        test: /\.(gif|png|jpe?g|svg)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192
            }
          },
          {
            loader: 'image-webpack-loader',
            options: {
              bypassOnDebug: true,
              mozjpeg: {
                progressive: true,
                quality: 40
              },
            },
          },
        ]
      },
      {
        test: /\.(css|less)$/,
        // 此处使用ExtractTextPlugin插件
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
            {
              loader: 'css-loader',
              options: {
                minimize: true
              }
            },
            {
              loader: 'less-loader'
            }
          ]
        })
      }
    ]
  },
  plugins: [
    // Plugin for code splitting, 抽离出公共部分单独打包
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common' // Specify the common bundle's name.
    }),
    // Plugin for code splitting, css单独打包
    new ExtractTextPlugin('style.css'),
  ]
};

其中使用公共部分需要用到webpack-merge, 需要npm安装一下这个包.
之后在webpack.dev.jswebpack.prod.js中分离开发环境和生产环境所用到的代码, 如:

// webpack.dev.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common');

module.exports = merge(common, {
  devtool: 'inline-source-map',
  devServer: {
    // 本地服务器所加载页面所在目录
    contentBase: '../',
    // 是否跳转, 单页面时使用ture
    historyApiFallback: true,
    // inline模式, 表示脚本将会在动态重载时被插入到bundle中
    inline: true,
    // 允许Hot Module Replacement功能
    hot: true
  },
  plugins: [
    // 也用于热加载, 配置后会显示更新的模块名称
    new webpack.NamedModulesPlugin(),
    // 热加载插件: npm install --save-dev babel-plugin-react-transform react-transform-hmr
    new webpack.HotModuleReplacementPlugin(),
  ]
});

此处在webpack.dev.js中单独抽离出了开发环境的代码, 需要注意的有以下几点:

  1. 其中使用了const common = require('./webpack.common');来将公共部分引入
  2. 公共引入部分使用了merge()去和单独的部分进行整合

同理, prod即生产环境也使用同样的方式进行整合, 如:

// webpack.prod.js
const webpack = require('webpack');
// 用于配置文件的合并
const merge = require('webpack-merge');
// js压缩插件
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
// 抽离出公共部分
const common = require('./webpack.common');

module.exports = merge(common, {
  plugins: [
    // 压缩Js
    new UglifyJSPlugin({
      sourceMap: true
    }),
    // react配置webpack生产模式
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production')
    })
  ]
});

四 NPM Scripts

最后一步要做的就是在package.json中加入开发环境和生产环境中不同的命令, 因为两种环境使用的配置文件不同, 所以要在命令中指定:

"scripts": {
    "start": "webpack-dev-server --open --port 3000 --config webpack.dev.js",
    "build": "BABEL_ENV=production webpack --config webpack.prod.js"
  },
  1. build即生产环境打包, 用--config指定了生产环境的配置文件
  2. start即在开发环境中使用, 在3000端口中打开webpack自带的开发服务器, 并且指定了开发环境的配置文件

你可能感兴趣的:(React,Webpack)