生产环境webapck.config.js配置详解

生产环境不同于开发环境在于,生产环境需要优化代码,保证在上线之后运行更快且兼容各大浏览器。以开发环境中css资源打包为例:先把css资源构建一个commmjs模块,形成一个字符串,接着在js文件中创建style标签将构建的字符串插入到style标签中,而如果样式资源很多,会导致js文件很大,同时由于浏览器加载Js文件的机制,势必影响性能,同时插入style标签内容可能也会导致闪屏的情况产生(由于css内容放在js文件中的,且html引入的也是js文件,当具体显示html文件的时候,会动态创建style标签,并将css内容插入到style标签中,同时将style标签插入到head标签中,最后页面显示css样式,而这个动态过程是在浏览器中完成,容易造成闪屏)。因此为了增加用户体验,提高代码运行速度以及兼容性,生产环境则需要解决上述问题,同时也包括:抽取出js文件中的css为单独文件,对代码进行压缩、处理兼容性问题等一系列问题,但是为什么不在开发环境就处理好呢?如果在开发环境中处理这些复杂的问题,会影响开发进度。

1.处理css文件
1.提取出css为单独文件

不同于将css文件写入到打包之后的js文件,此时的css抽取到了单独的css文件中,并通过link进行引入,使得闪屏问题进一步解决,同时js文件大小减小。

用法:

  • 下载mini-css-extract-plugin

    npm i -s mini-css-extract-plugin
    
  • 引入

    const miniCssExtractPlugin = require('mini-css-extract-plugin');
    
  • 配置

     // loader配置
      module: {
        rules: [
          // css配置
          {
            test: /\.css$/,
            use: [
              // 让loader取代style-loader,作用:提取js中的css成单独文件,也即类似于截获,当css文件整合到Js文件后,
              // 不再是通过style-loader将js文件中的代码插入到style标签,也是抽取出来形成单独的文件
              miniCssExtractPlugin.loader,
              'css-loader'
            ]
          }
        ]
      },
      // plugins配置
      plugins: [
        new htmlWebpackPlugin({
          template: './src/webpack_02.html'
        }),
        new miniCssExtractPlugin({
          // 给css文件重命名
          filename: 'css/main.css'
        })
      ]
    
2.css兼容性处理

采取的是postcss,而postcss的实现需要两个插件:postcss-loader postcss-preset-env

下载:

npm i -s postcss-loader postcss-preset-env

postcss-preset-env作用:帮postcss找到package.json中browserlist里面的配置,通过配置加载指定的css兼容性样式

配置:

  rules: [
      // css配置
     {
        test: /\.css$/,
        use: [
          miniCssExtractPlugin.loader,
          'css-loader',
          // postcss-loader默认配置 'postcss-loader' 但是此处修改设置
          // 修改配置
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                // 默认写法
                ident: 'postcss',
                // 方法一:这样写没有兼容性处理代码
                // plugins: () => [
                //   require('postcss-preset-env')()
                // ]
                 // 方法二:这样写有兼容性处理代码
                // plugins: [
                //   require('postcss-preset-env')
                // ]
                // 方法三:或者将插件引入写在单独的配置js中
                config: './postcss.config.js'
              }
            }
          }

        ]
      }
    ]

# 方法三的配置文件
# postcss.config.js

module.exports = {
  plugins: [
    require('postcss-preset-env')
  ]
}

package.json配置

// 可以去官网查看配置含义
"browserslist": {
    // 开发环境 需要用此环境 需要设置node环境变量:process.env.NODE_ENV = development
  "development": [
      // 兼容最近一次版本的浏览器
    "last 1 chrome version",
    "last 1 firefox version",
    "last 1 safari version"
  ],
    // 生产环境 默认是看生产环境 和webpack.config.js中的mode没有关系
  "production": [
      // 99.8的浏览器兼容
    ">0.2%",
      // 不用考虑已不用的浏览器和op_mini
    "not dead",
    "not op_mini all"
  ]
}

由于package.json默认是看生产环境,因此需要在webpack中设置node环境变量来进行查看开发环境:

process.env.NODE_ENV = 'development';
module.exports = {
    ...
}

原来的css文件:

#box2 {
    width: 200px;
    height: 200px;
    background-color: #454545;
    display: flex;
    backface-visibility: hidden;
}

经过兼容性处理之后的css文件:

#box2 {
    width: 200px;
    height: 200px;
    background-color: #454545;
    display: -webkit-flex;
    display: flex;
    -webkit-backface-visibility: hidden;
            backface-visibility: hidden;
}

3.压缩css

通过optimize-css-assets-webpack-plugin插件完成

const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); 
// plugins配置
  plugins: [
    // 压缩css
    new OptimizeCssAssetsWebpackPlugin()
  ],
4.完整css处理代码
/**
 生产环境搭建
 **/
const { resolve } = require('path')
const miniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

// 配置开发环境
process.env.NODE_ENV = 'development';

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/index.js',
    path: resolve(__dirname, 'bulid')
  },
  // loader配置
  module: {
    rules: [
      // css配置
      {
        test: /\.css$/,
        use: [
          miniCssExtractPlugin.loader,
          'css-loader',
          // postcss-loader默认配置 'postcss-loader' 但是此处修改设置
          // 修改配置
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                // 默认写法
                ident: 'postcss',
                // 这样写没有兼容性处理代码
                // plugins: () => [
                //   require('postcss-preset-env')()
                // ]
                // 这样写有兼容性处理代码
                // plugins: [
                //   require('postcss-preset-env')
                // ]
                // 或者将插件引入写在单独的配置js中
                config: './postcss.config.js'
              }
            }
          }

        ]
      }
    ]
  },
  // plugins配置
  plugins: [
    new miniCssExtractPlugin({
      // 给css文件重命名
      filename: 'css/main.css'
    }),
    // 压缩css
    new OptimizeCssAssetsWebpackPlugin()
  ],
  // 模式
  mode: 'development'
}

# postcss.config.js
module.exports = {
  plugins: [
    require('postcss-preset-env')
  ]
}
2.js处理
1.js语法检查eslint

为了统一规范代码书写,可以使用eslint来进行约束。

首先使用eslint,采用eslint-loader来实现,但是eslint-loader依赖于eslint,因此两者都需要下载

npm i -s eslint-loader eslint

注意:eslint只检查js内容,且只需要检查自己写的源代码,第三方库不用检查,因此配置如下:

 module: {
    rules: [
      {
        // 只检查js文件内容
        test: /\.js$/,
        // 不用检查第三方库
        exclude: /node_modules/,
        loader: 'eslint-loader'
      }
    ]
  },

但是仅仅是以上配置,webpack打包时不知道如何去检查,则需要配置检查规则,一般使用:airbnb,查看npmjs.com发现eslint-config-airbnb适合es6语法但是也适合react,如果不需要使用react,则可以使用eslint-config-airbnb-base,其也适合es6+语法。查看其官网可以看到要下载三个包:eslint-plugin-import、eslint、eslint-config-airbnb-base

npm i -s eslint-plugin-import eslint eslint-config-airbnb-base

此外下载完成之后,由于此规则的使用是通过在package.jsoneslintConfig中进行设置来进一步使用的,因此:

"eslintConfig": {
    "extends": "airbnb-base"
  }

测试js文件:

// 该文件格式故意写的不规范
function add (x,y) {
  console.log(x+y);
}

add(2,3);

运行webpack

结果如下:可以看到有很多不规范的地方

E:\前端\nodejs\实践\js\webpack_01\src\index.js
  1:1   error    Expected linebreaks to be 'LF' but found 'CRLF'                  linebreak-style
  1:1   error    Too many blank lines at the beginning of file. Max of 0 allowed  no-multiple-empty-lines
  2:13  error    Unexpected space before function parentheses                     space-before-function-paren
  2:16  error    A space is required after ','                                    comma-spacing
  2:21  error    Expected linebreaks to be 'LF' but found 'CRLF'                  linebreak-style
  3:3   warning  Unexpected console statement                                     no-console
  3:16  error    Operator '+' must be spaced                                      space-infix-ops
  3:20  error    Expected linebreaks to be 'LF' but found 'CRLF'                  linebreak-style
  4:2   error    Expected linebreaks to be 'LF' but found 'CRLF'                  linebreak-style
  5:1   error    Expected linebreaks to be 'LF' but found 'CRLF'                  linebreak-style
  6:6   error    A space is required after ','                                    comma-spacing
  6:10  error    Newline required at end of file but not found                    eol-last

✖ 12 problems (11 errors, 1 warning)
  11 errors and 0 warnings potentially fixable with the `--fix` option.

但是一个一个的修改很麻烦,因此可以配置自动修复eslint错误

 module: {
    rules: [
      {
        // 只检查js文件内容
        test: /\.js$/,
        // 不用检查第三方库
        exclude: /node_modules/,
        loader: 'eslint-loader',
        options: {
          // 自动修复eslint错误
          fix: true
        }
      }
    ]
  }

修复之后的index.js

function add(x, y) {
  console.log(x + y);
}

add(2, 3);

但是出现以下的警告信息:

2:3  warning  Unexpected console statement  no-console
✖ 1 problem (0 errors, 1 warning)

原因:不期望出现console信息,但是开发阶段为了测试又不得不去console,但是为了不出现这些警告信息,可以设置eslint配置,即:在需要检查的js文件中会出现警告的一行上方写上:// eslint-disable-next-line,即可不会出现警告信息了

function add(x, y) {
  // 表示:下一行eslint所以规则都失效(下一行不进行eslint检查)
  // eslint-disable-next-line
  console.log(x + y);
}
add(2, 3);
2.js兼容性处理

在实际的打包后输出的代码是没有进行兼容性处理的,因此对于不支持es6语法的浏览器是不友好的。兼容性处理通过babel-loader来实现,其依赖于@babel/core

npm i -s babel-loader @babel/core

兼容性处理有三种方式:

  • 基本Js兼容性处理

    问题:使用此方式只能转换基本语法,比如:promise不能转换

    @babel/preset-env

    npm i -s @babel/preset-env
    

    配置:

    module: {
      rules: [
        /*
        1. 基本兼容性处理
          @babel/preset-env
         */
        {
          // 对js文件进行兼容性处理
          test: /\.js$/,
          // 不对第三方包进行兼容性处理
          exclude: /node_modules/,
          loader: 'babel-loader',
          options: {
            // 预设:指示babel做怎么样的兼容性处理
             presets: [["@babel/preset-env", { "targets": "defaults" }]]
              // 此处使用 { "targets": "defaults" }的原因:preset-env的行为与browserslist不同:当在Babel或browserslist配置中没有找到目标时,它不会使用defaults查询。如果你想使用defaults查询,你需要显式地将它作为目标传递:
          }
        }
      ]
    },
    

    测试js

    const fn1 = (x, y) => {
      return x + y;
    }
    console.log(fn1(1,2));
    

    打包之后的Index.js

    eval("var fn1 = function fn1(x, y) {\n  return x + y;\n};\n\nconsole.log(fn1(1, 2));\n\n//# sourceURL=webpack:///./src/index.js?");
    

    并且

  • 全部js统一兼容性处理

    工作机制:预先定义好所有可能的方法,当不兼容的时候,将该方法挂载到对应的对象上,比如Object中不兼容,直接挂载到Object对象上。

    问题:当只需要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大

    @babel/polyfill

    npm i -s @babel/polyfill
    

    用法:直接在需要兼容性的js文件头部进行引入即可:

    import '@babel/polyfill';
    
    const fn1 = (x, y) => {
      return x + y;
    }
    console.log(fn1(1,2));
    

    结果:进行兼容性处理了,但是打包后的Index.js文件大小从原来的1.25kb转变为:437kb

  • 部分兼容性处理

    按需加载需要的兼容性处理代码

    core-js

    npm i -s core-js
    

    不同于@babel/polyfill配置中使用targets:faluts默认配置,此处需要对其进行按需配置。

    module: {
      rules: [
        {
          // 对js文件进行兼容性处理
          test: /\.js$/,
          // 不对第三方包进行兼容性处理
          exclude: /node_modules/,
          loader: 'babel-loader',
          options: {
            // 预设:指示babel做怎么样的兼容性处理
            presets: [["@babel/preset-env", {
              // 指定按需加载
              useBuiltIns: 'usage',
              // 指定core-js版本 一般下载的时候可以看到版本
              corejs: {
                version: 3
              },
              // 指定兼容性做到哪个版本浏览器
              targets: {
                chrome: '60',
                firefox: '60',
                ie: '9',
                safari: '10',
                edge: '17'
              }
            }]]
          }
        }
      ]
    },
    

    注意:使用的时候要把import '@babel/polyfill';给去掉

    打包结果:index.js大小:1.29kb

3.js压缩处理

由于webpack在mode='production'的时候,内置了插件处理压缩js,因此只需要将mode改为production即可实现

3.html处理

由于html标签识别就显示出来,不识别就不显示出来。因此没有兼容性处理。但是可以对其进行压缩处理。

配置:

plugins: [
  new htmlWebpackPlugin({
    template: './src/webpack_02.html',
    filename: 'index.html',
    // 压缩html配置
    minify: {
      // 移除空格
      collapseWhitespace: true,
    //  移除注释
      removeComments: true
    }
  })
],
生产环境总写:
/*
 生产环境:优化代码
 */
const { resolve } = require('path');
const miniCssExtractPlugin = require('mini-css-extract-plugin');
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const htmlWebpackPlugin = require('html-webpack-plugin');

// 公共处理css
const commonCssPlugin = [
  miniCssExtractPlugin.loader,
  'css-loader',
  {
    loader: 'postcss-loader',
    options: {
      postcssOptions: {
        indent: 'postcss',
        plugins: [
          require('postcss-preset-env')
        ]
      }
    }
  }
]

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/index.js',
    path: resolve(__dirname,'build')
  },
  module: {
    rules: [
      {
        // css配置
        test: /\.css$/,
        use: [...commonCssPlugin]
      },
      {
        // less配置
        test: /\.less$/,
        use: [...commonCssPlugin,'less-loader']
      },
      /**
       * 正常来讲,一个文件只能被一个loader处理,当一个文件要被多个loader处理,
       * 那么一定要指定loader执行的先后顺序:
       * 先执行eslint 再执行babel (原因:当通过语法之后再进行兼容性处理操作)
       */
      {
        // js配置 eslint
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // js 配置 babel
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            [
            '@babel/preset-env',
            {
              useBuiltIns: 'usage',
              corejs: {
                version: 3
              },
              targets: {
                chrome: '60',
                firefox: '60',
                ie: '9',
                safari: '10',
                edge: '17'
              }
            }
            ]
          ]
        }
      },
      {
        // 处理图片资源
        test: /\.(jpg|PNG|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        // 处理html中的图片资源
        test: /\.html/,
        loader: 'html-loader',
        options: {
          esModule: false
        }
      }
    ]
  },
  plugins: [
    new miniCssExtractPlugin({
      filename: 'css/index.css'
    }),
    new optimizeCssAssetsWebpackPlugin(),
    // 处理html
    new htmlWebpackPlugin({
      template: './src/webpack_02.html',
      filename: 'index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
}

你可能感兴趣的:(生产环境webapck.config.js配置详解)