生产环境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?");
    

    并且<ie10都能正常显示

  • 全部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'
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容