webpack 基础_优化

187 阅读17分钟

webpack 基础_优化

webpack 优化配置

开发环境性能优化

  • 优化打包构建速度
  • 优化代码调试

生产环境性能优化

  • 优化打包构建速度
  • 优化代码运行的性能

HMR

hot module replacement 热模块替换/模块热替换

作用:一个模块发生变化,只会重新打包这一个模块(而不是所有模块),提升构建速度

样式文件:

  • 可以使用 HMR功能;因为 style-loader内部实现了此功能

js文件:

  • 默认不能使用 HMR功能

  • 解决:需要修改 js代码,添加支持 HMR功能的代码

  • 注意:HMR功能对 js的处理,只能处理非入口 js文件的其它文件

    import '../css/iconfont.css';
    import '../css/a.css';
    import '../css/b.less';
    
    import print from './print';
    
    const add = (x, y) => x + y;
    console.log(add(1, 2));
    
    if (module.hot) {
      // 一旦 module.hot为 true,说明开启了 HMR功能 -> 让 HMR功能代码生效
      module.hot.accept('./print.js', () => {
       /**
       * 方法会监听 print.js文件变化,一旦发生变化,其它模块不会重新打包
       * 会执行后面的回调函数
       */
        print();
      });
    }
    

html文件:

  • 默认不能使用 HMR功能,同时会导致问题,HTML文件不能热更新了(程序中只有一个 HTML文件,不需要做 HMR功能)
  • 解决:修改 entry入口,将 HTML文件引入

编码:

  • 修改配置文件:在 devServer中添加属性 hot: true
// nodejs
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 将 css从 js中提取出来,并到单独的文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// css代码压缩
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

// 定义 nodejs环境变量:决定使用 browserslist的哪个环境
// process.env.NODE_ENV = 'production'
// process.env.NODE_ENV = 'development'

// loader复用
const commonCSSLoader = [
  'style-loader',
  // 将 css代码提取到一个指定的文件中
  // MiniCssExtractPlugin.loader,
  // 将 css加载到 js中
  'css-loader',
  /**
   * css兼容性处理:只能针对 css代码来操作
   * postcss ->
   * postcss-loader ->
   * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
   */
  {
    /**
     * 需要在 package.json中定义 browserslist里面的配置
     */
    loader: 'postcss-loader',
    options: {
      // 告诉它要做哪些兼容性配置
      ident: 'postcss',
      plugins: () => [
        /**
         * postcss的插件
         * 帮助 postcss找到 package.json中 browserslist里面的配置,
         * 通过配置加载指定的 css兼容性样式
         */
        require('postcss-preset-env')(),
      ],
    },
  },
];

// commonjs
module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      // css代码处理
      {
        test: /\.css$/,
        use: [
          ...commonCSSLoader,
          // // 将 css代码提取到一个指定的文件中
          // MiniCssExtractPlugin.loader,
          // // 将 css加载到 js中
          // 'css-loader',
          // /**
          //  * css兼容性处理:只能针对 css代码来操作
          //  * postcss ->
          //  * postcss-loader ->
          //  * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
          //  */
          // {
          //   /**
          //    * 需要在 package.json中定义 browserslist里面的配置
          //    */
          //   loader: 'postcss-loader',
          //   options: {
          //     // 告诉它要做哪些兼容性配置
          //     ident: 'postcss',
          //     plugins: () => [
          //       /**
          //        * postcss的插件
          //        * 帮助 postcss找到 package.json中 browserslist里面的配置,
          //        * 通过配置加载指定的 css兼容性样式
          //        */
          //       require('postcss-preset-env')()
          //     ]
          //   }
          // }
        ],
      },
      {
        test: /\.less$/,
        use: [
          ...commonCSSLoader,
          // MiniCssExtractPlugin.loader,
          // 'css-loader',
          'less-loader',
        ],
      },
      /**
       * 正常来讲,一个文件只能被一个 loader处理;
       * 当一个文件要被多个 loader处理,那一定要指定 loader执行的先后顺序
       * 如下面的 .js文件:应该先执行 eslint再执行 bable,通过 enforce属性指定
       */
      // js语法检查:规范项目代码,检查常见语法错误
      {
        /**
         * 只检查源代码,忽略第三发库
         * eslint-loader
         * eslint
         * 在 package.json的 eslintConfig中设置检查规则,推荐 airbnb
         */
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行此 loader
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          // 自动修复 eslint错误
          fix: true,
        },
      },
      {
        // js兼容性处理:babel-loader、@babel/preset-env、@babel/core
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          // 预设:指示 babel做怎样的兼容处理
          presets: [
            [
              // 只能作简单的语法兼容处理
              '@babel/preset-env',
              // corejs可以作复杂的语法兼容处理
              {
                // 按需加载
                useBuiltIns: 'usage',
                // 指定 core-js版本
                corejs: {
                  version: 3,
                },
                // 指定兼容性做到哪个版本的浏览器
                targets: {
                  chrome: '60',
                  firefox: '50',
                  safari: '10',
                  edge: '17',
                  ie: '9',
                },
              },
            ],
          ],
        },
      },
      // 处理图片资源
      {
        test: /\.(png|gif|jpg|jpeg)$/,
        loader: 'url-loader',
        options: {
          // 对于图片小于 8-12kb的可以进行 base64处理
          limit: 8 * 1024,
          // 关闭 ES6解析,使用 commonjs解析
          esModule: false,
          name: '[hash:10].[ext]',
          outputPath: 'imgs',
        },
      },
      // 处理 HTML中的 img资源
      {
        test: /\.html$/,
        // 处理 HTML中的 img资源
        loader: 'html-loader',
      },
      // 处理其它资源
      {
        exclude: /\.(html|js|css|less|png|gif|jpg|jpeg)/,
        // 将其它资源原封不动的输出
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media',
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // html代码压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      // 对提取出的 css代码放到统一的文件中,并命名
      filename: 'css/built.css',
    }),
    // css代码压缩
    new OptimizeCssAssetsWebpackPlugin(),
  ],
  // mode: 'production',
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3030,
    open: true,
    // 开启 HRM功能
    hot: true,
  },
};

  • 运行指令: webpack

source-map

一种提供源代码到构建后代码映射的技术,非常利于调试代码;例如构建后代码出错,通过映射关系可以追踪源代码的错误

开发环境:速度更快,调试更友好

速度快(eval > inline > cheap > ...)

  • eval-cheap-source-map
  • eval-source-map

调试更友好

  • source-map
  • cheap-module-source-map
  • cheap-source-map

总结:使用 eval-source-map(调试更好)、eval-cheap-module-source-map(性能更好)

生产环境:源代码要不要隐藏,调试要不要更友好

内联会让代码体积更大,所以在生产环境不用内联

隐藏源代码

  • nosource-source-map:全部隐藏
  • hidden-source-map:只隐藏源代码

调试友好

  • source-map
  • cheap-module-source-map

总结:使用 source-map、cheap-module-source-map

  • 修改配置文件:devtool: 'source-map'
// nodejs
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 将 css从 js中提取出来,并到单独的文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// css代码压缩
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

// 定义 nodejs环境变量:决定使用 browserslist的哪个环境
// process.env.NODE_ENV = 'production'
// process.env.NODE_ENV = 'development'

// loader复用
const commonCSSLoader = [
  'style-loader',
  // 将 css代码提取到一个指定的文件中
  // MiniCssExtractPlugin.loader,
  /**
   * 问题:
   * MiniCssExtractPlugin.loader当前不能将 less文件单独提取出来放到指定的文件中
   */
  // 将 css加载到 js中
  'css-loader',
  /**
   * css兼容性处理:只能针对 css代码来操作
   * postcss ->
   * postcss-loader ->
   * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
   */
  {
    /**
     * 需要在 package.json中定义 browserslist里面的配置
     */
    loader: 'postcss-loader',
    options: {
      // 告诉它要做哪些兼容性配置
      ident: 'postcss',
      plugins: () => [
        /**
         * postcss的插件
         * 帮助 postcss找到 package.json中 browserslist里面的配置,
         * 通过配置加载指定的 css兼容性样式
         */
        require('postcss-preset-env')(),
      ],
    },
  },
];

// commonjs
module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      // css代码处理
      {
        test: /\.css$/,
        use: [
          ...commonCSSLoader,
          // // 将 css代码提取到一个指定的文件中
          // MiniCssExtractPlugin.loader,
          // // 将 css加载到 js中
          // 'css-loader',
          // /**
          //  * css兼容性处理:只能针对 css代码来操作
          //  * postcss ->
          //  * postcss-loader ->
          //  * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
          //  */
          // {
          //   /**
          //    * 需要在 package.json中定义 browserslist里面的配置
          //    */
          //   loader: 'postcss-loader',
          //   options: {
          //     // 告诉它要做哪些兼容性配置
          //     ident: 'postcss',
          //     plugins: () => [
          //       /**
          //        * postcss的插件
          //        * 帮助 postcss找到 package.json中 browserslist里面的配置,
          //        * 通过配置加载指定的 css兼容性样式
          //        */
          //       require('postcss-preset-env')()
          //     ]
          //   }
          // }
        ],
      },
      {
        test: /\.less$/,
        use: [
          ...commonCSSLoader,
          // MiniCssExtractPlugin.loader,
          // 'css-loader',
          'less-loader',
        ],
      },
      /**
       * 正常来讲,一个文件只能被一个 loader处理;
       * 当一个文件要被多个 loader处理,那一定要指定 loader执行的先后顺序
       * 如下面的 .js文件:应该先执行 eslint再执行 bable,通过 enforce属性指定
       */
      // js语法检查:规范项目代码,检查常见语法错误
      // {
      //   /**
      //    * 只检查源代码,忽略第三发库
      //    * eslint-loader
      //    * eslint
      //    * 在 package.json的 eslintConfig中设置检查规则,推荐 airbnb
      //    */
      //   test: /\.js$/,
      //   exclude: /node_modules/,
      //   // 优先执行此 loader
      //   enforce: 'pre',
      //   loader: 'eslint-loader',
      //   options: {
      //     // 自动修复 eslint错误
      //     fix: true,
      //   },
      // },
      
      // {
      //   // js兼容性处理:babel-loader、@babel/preset-env、@babel/core
      //   test: /\.js$/,
      //   exclude: /node_modules/,
      //   loader: 'babel-loader',
      //   options: {
      //     // 预设:指示 babel做怎样的兼容处理
      //     presets: [
      //       [
      //         // 只能作简单的语法兼容处理
      //         '@babel/preset-env',
      //         // corejs可以作复杂的语法兼容处理
      //         {
      //           // 按需加载
      //           useBuiltIns: 'usage',
      //           // 指定 core-js版本
      //           corejs: {
      //             version: 3,
      //           },
      //           // 指定兼容性做到哪个版本的浏览器
      //           targets: {
      //             chrome: '60',
      //             firefox: '50',
      //             safari: '10',
      //             edge: '17',
      //             ie: '9',
      //           },
      //         },
      //       ],
      //     ],
      //   },
      // },
      
      // 处理图片资源
      {
        test: /\.(png|gif|jpg|jpeg)$/,
        loader: 'url-loader',
        options: {
          // 对于图片小于 8-12kb的可以进行 base64处理
          limit: 8 * 1024,
          // 关闭 ES6解析,使用 commonjs解析
          esModule: false,
          name: '[hash:10].[ext]',
          outputPath: 'imgs',
        },
      },
      // 处理 HTML中的 img资源
      {
        test: /\.html$/,
        // 处理 HTML中的 img资源
        loader: 'html-loader',
      },
      // 处理其它资源
      {
        exclude: /\.(html|js|css|less|png|gif|jpg|jpeg)/,
        // 将其它资源原封不动的输出
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media',
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // html代码压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      // 对提取出的 css代码放到统一的文件中,并命名
      filename: 'css/built.css',
    }),
    // css代码压缩
    new OptimizeCssAssetsWebpackPlugin(),
  ],
  // mode: 'production',
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3030,
    open: true,
    // 开启 HRM功能
    hot: true,
  },
  /**
   * source-map的设置:
   * [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
   * source-map:内联
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * inline-source-map:内联
   *  1.将生成的 .map文件放在 .js文件中
   *  2.只生成一个内联的 source-map文件
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * hidden-source-map(防止源代码泄露):外联
   *  1.将生成的一个 source-map文件放在单独的 .map文件中
   *  错误提示:错误代码错误原因,但是没有错误位置,
   *  不能追踪源代码错误,只能提示到构建后代码的错误位置
   * eval-source-map:内联
   *  1.针对每一个文件都生成一个单独的 source-map文件,放在 .js文件的 eval函数中
   *  错误提示:错误代码准确信息 和 源代码的错误位置
   * nosource-source-map(防止源代码泄露):外联
   *  错误提示:错误代码准确信息, 但是没有任何源代码信息(可以防止源代码泄露)
   * cheap-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,只能精确到行
   * cheap-module-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,
   *  module会将 loader的 source-map加入
   * 
   */
  // 开启构建后代码与源码的映射
  devtool: 'eval-source-map' // development
  // devtool: 'source-map' // production
};
  • 运行命令:npx webpack-dev-server

oneOf

提升打包构建速度,阻止每种类型的文件都被以下 loader判断一次

  • 修改配置
// nodejs
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 将 css从 js中提取出来,并到单独的文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// css代码压缩
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

// 定义 nodejs环境变量:决定使用 browserslist的哪个环境
// process.env.NODE_ENV = 'production'
// process.env.NODE_ENV = 'development'

// loader复用
const commonCSSLoader = [
  'style-loader',
  // 将 css代码提取到一个指定的文件中
  // MiniCssExtractPlugin.loader,
  /**
   * 问题:
   * MiniCssExtractPlugin.loader当前不能将 less文件单独提取出来放到指定的文件中
   */
  // 将 css加载到 js中
  'css-loader',
  /**
   * css兼容性处理:只能针对 css代码来操作
   * postcss ->
   * postcss-loader ->
   * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
   */
  {
    /**
     * 需要在 package.json中定义 browserslist里面的配置
     */
    loader: 'postcss-loader',
    options: {
      // 告诉它要做哪些兼容性配置
      ident: 'postcss',
      plugins: () => [
        /**
         * postcss的插件
         * 帮助 postcss找到 package.json中 browserslist里面的配置,
         * 通过配置加载指定的 css兼容性样式
         */
        require('postcss-preset-env')(),
      ],
    },
  },
];

// commonjs
module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      {
        /**
         * 只检查源代码,忽略第三发库
         * eslint-loader
         * eslint
         * 在 package.json的 eslintConfig中设置检查规则,推荐 airbnb
         */
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行此 loader
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          // 自动修复 eslint错误
          fix: true,
        },
      },
      {
        /**
         * oneOf:提升打包构建速度,阻止每种类型的文件都被以下 loader判断一次
         * 在 oneOf中的 loader只会匹配一个
         * 注意:在 oneOf中不能有两个及以上的配置处理同一种类型的文件
         *    如果有,需要拿到 oneOf外边处理
         */
        oneOf: [
          // css代码处理
          {
            test: /\.css$/,
            use: [
              ...commonCSSLoader,
              /**
               * 
               */
              // // 将 css代码提取到一个指定的文件中
              // MiniCssExtractPlugin.loader,
              // // 将 css加载到 js中
              // 'css-loader',
              // /**
              //  * css兼容性处理:只能针对 css代码来操作
              //  * postcss ->
              //  * postcss-loader ->
              //  * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
              //  */
              // {
              //   /**
              //    * 需要在 package.json中定义 browserslist里面的配置
              //    */
              //   loader: 'postcss-loader',
              //   options: {
              //     // 告诉它要做哪些兼容性配置
              //     ident: 'postcss',
              //     plugins: () => [
              //       /**
              //        * postcss的插件
              //        * 帮助 postcss找到 package.json中 browserslist里面的配置,
              //        * 通过配置加载指定的 css兼容性样式
              //        */
              //       require('postcss-preset-env')()
              //     ]
              //   }
              // }
            ],
          },
          {
            test: /\.less$/,
            use: [
              ...commonCSSLoader,
              // MiniCssExtractPlugin.loader,
              // 'css-loader',
              'less-loader',
            ],
          },
          /**
           * 正常来讲,一个文件只能被一个 loader处理;
           * 当一个文件要被多个 loader处理,那一定要指定 loader执行的先后顺序
           * 如下面的 .js文件:应该先执行 eslint再执行 bable,通过 enforce属性指定
           */
          // js语法检查:规范项目代码,检查常见语法错误

          {
            // js兼容性处理:babel-loader、@babel/preset-env、@babel/core
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              // 预设:指示 babel做怎样的兼容处理
              presets: [
                [
                  // 只能作简单的语法兼容处理
                  '@babel/preset-env',
                  // corejs可以作复杂的语法兼容处理
                  {
                    // 按需加载
                    useBuiltIns: 'usage',
                    // 指定 core-js版本
                    corejs: {
                      version: 3,
                    },
                    // 指定兼容性做到哪个版本的浏览器
                    targets: {
                      chrome: '60',
                      firefox: '50',
                      safari: '10',
                      edge: '17',
                      ie: '9',
                    },
                  },
                ],
              ],
            },
          },

          // 处理图片资源
          {
            test: /\.(png|gif|jpg|jpeg)$/,
            loader: 'url-loader',
            options: {
              // 对于图片小于 8-12kb的可以进行 base64处理
              limit: 8 * 1024,
              // 关闭 ES6解析,使用 commonjs解析
              esModule: false,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
            },
          },
          // 处理 HTML中的 img资源
          {
            test: /\.html$/,
            // 处理 HTML中的 img资源
            loader: 'html-loader',
          },
          // 处理其它资源
          {
            exclude: /\.(html|js|css|less|png|gif|jpg|jpeg)/,
            // 将其它资源原封不动的输出
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]',
              outputPath: 'media',
            },
          },
        ],
      }
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // html代码压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      // 对提取出的 css代码放到统一的文件中,并命名
      filename: 'css/built.css',
    }),
    // css代码压缩
    new OptimizeCssAssetsWebpackPlugin(),
  ],
  // mode: 'production',
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3030,
    open: true,
    // 开启 HRM功能
    hot: true,
  },
  /**
   * source-map的设置:
   * [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
   * source-map:内联
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * inline-source-map:内联
   *  1.将生成的 .map文件放在 .js文件中
   *  2.只生成一个内联的 source-map文件
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * hidden-source-map(防止源代码泄露):外联
   *  1.将生成的一个 source-map文件放在单独的 .map文件中
   *  错误提示:错误代码错误原因,但是没有错误位置,
   *  不能追踪源代码错误,只能提示到构建后代码的错误位置
   * eval-source-map:内联
   *  1.针对每一个文件都生成一个单独的 source-map文件,放在 .js文件的 eval函数中
   *  错误提示:错误代码准确信息 和 源代码的错误位置
   * nosource-source-map(防止源代码泄露):外联
   *  错误提示:错误代码准确信息, 但是没有任何源代码信息(可以防止源代码泄露)
   * cheap-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,只能精确到行
   * cheap-module-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,
   *  module会将 loader的 source-map加入
   * 
   */
  // 开启构建后代码与源码的映射
  devtool: 'eval-source-map' // development
  // devtool: 'source-map' // production
};

缓存 -23

babel缓存:cacheDirectory: true

将 babel编译的 js文件进行缓存,当第二次及以后打包构建时使用缓存,只对有修改的 js文件进行重新编译;使得除第一次外以后的打包更快

  {
    // js兼容性处理:babel-loader、@babel/preset-env、@babel/core
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
      // 预设:指示 babel做怎样的兼容处理
      presets: [
        [
          // 只能作简单的语法兼容处理
          '@babel/preset-env',
          // corejs可以作复杂的语法兼容处理
          {
            // 按需加载
            useBuiltIns: 'usage',
            // 指定 core-js版本
            corejs: {
              version: 3,
            },
            // 指定兼容性做到哪个版本的浏览器
            targets: {
              chrome: '60',
              firefox: '50',
              safari: '10',
              edge: '17',
              ie: '9',
            },
          },
        ],
      ],
      /**
       * 开启 babel缓存
       * 第二次构建时,会读取缓存,提升构建速度
       */
      cacheDirectory: true
    },
  },

文件资源缓存

hash:(有问题)

每次 webpack构建时会生成一个唯一的 hash值

  • 问题:因 js和 css同时使用一个 hash值;如果重新打包,会导致所有缓存失效(只改变一个文件的情况下)

chunkhash:(有问题)

所有根据入口文件引入的文件会生成一个 chunk;

根据 chunk生成的 hash值;如果打包来源于同一个 chunk,那 hash值就一样

  • 问题:js和 css的 hash值相同,因为 css是在 js中提取出来的,属于同一个 chunk

contenthash:(解决)

根据文件的内容来生成 hash值,所以不同的文件 hash值一定不同

  • 让代码上线后,缓存更好使用
结果:
  • 修改配置文件
// nodejs
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 将 css从 js中提取出来,并到单独的文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// css代码压缩
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

// 定义 nodejs环境变量:决定使用 browserslist的哪个环境
// process.env.NODE_ENV = 'production'
// process.env.NODE_ENV = 'development'

// loader复用
const commonCSSLoader = [
  // 'style-loader',
  // 将 css代码提取到一个指定的文件中
  MiniCssExtractPlugin.loader,
  /**
   * 问题:
   * MiniCssExtractPlugin.loader当前不能将 less文件单独提取出来放到指定的文件中
   */
  // 将 css加载到 js中
  'css-loader',
  /**
   * css兼容性处理:只能针对 css代码来操作
   * postcss ->
   * postcss-loader ->
   * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
   */
  {
    /**
     * 需要在 package.json中定义 browserslist里面的配置
     */
    loader: 'postcss-loader',
    options: {
      // 告诉它要做哪些兼容性配置
      ident: 'postcss',
      plugins: () => [
        /**
         * postcss的插件
         * 帮助 postcss找到 package.json中 browserslist里面的配置,
         * 通过配置加载指定的 css兼容性样式
         */
        require('postcss-preset-env')(),
      ],
    },
  },
];

// commonjs
module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    /**
     * 给文件添加 hash值,防止缓存:[hash:8]
     */
    filename: 'js/built.[contenthash:8].js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      {
        /**
         * 只检查源代码,忽略第三发库
         * eslint-loader
         * eslint
         * 在 package.json的 eslintConfig中设置检查规则,推荐 airbnb
         */
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行此 loader
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          // 自动修复 eslint错误
          fix: true,
        },
      },
      {
        /**
         * oneOf:提升打包构建速度,阻止每种类型的文件都被以下 loader判断一次
         * 在 oneOf中的 loader只会匹配一个
         * 注意:在 oneOf中不能有两个及以上的配置处理同一种类型的文件
         *    如果有,需要拿到 oneOf外边处理
         */
        oneOf: [
          // css代码处理
          {
            test: /\.css$/,
            use: [
              ...commonCSSLoader,
              /**
               * 
               */
              // // 将 css代码提取到一个指定的文件中
              // MiniCssExtractPlugin.loader,
              // // 将 css加载到 js中
              // 'css-loader',
              // /**
              //  * css兼容性处理:只能针对 css代码来操作
              //  * postcss ->
              //  * postcss-loader ->
              //  * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
              //  */
              // {
              //   /**
              //    * 需要在 package.json中定义 browserslist里面的配置
              //    */
              //   loader: 'postcss-loader',
              //   options: {
              //     // 告诉它要做哪些兼容性配置
              //     ident: 'postcss',
              //     plugins: () => [
              //       /**
              //        * postcss的插件
              //        * 帮助 postcss找到 package.json中 browserslist里面的配置,
              //        * 通过配置加载指定的 css兼容性样式
              //        */
              //       require('postcss-preset-env')()
              //     ]
              //   }
              // }
            ],
          },
          {
            test: /\.less$/,
            use: [
              ...commonCSSLoader,
              // MiniCssExtractPlugin.loader,
              // 'css-loader',
              'less-loader',
            ],
          },
          /**
           * 正常来讲,一个文件只能被一个 loader处理;
           * 当一个文件要被多个 loader处理,那一定要指定 loader执行的先后顺序
           * 如下面的 .js文件:应该先执行 eslint再执行 bable,通过 enforce属性指定
           */
          // js语法检查:规范项目代码,检查常见语法错误

          {
            // js兼容性处理:babel-loader、@babel/preset-env、@babel/core
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              // 预设:指示 babel做怎样的兼容处理
              presets: [
                [
                  // 只能作简单的语法兼容处理
                  '@babel/preset-env',
                  // corejs可以作复杂的语法兼容处理
                  {
                    // 按需加载
                    useBuiltIns: 'usage',
                    // 指定 core-js版本
                    corejs: {
                      version: 3,
                    },
                    // 指定兼容性做到哪个版本的浏览器
                    targets: {
                      chrome: '60',
                      firefox: '50',
                      safari: '10',
                      edge: '17',
                      ie: '9',
                    },
                  },
                ],
              ],
              /**
               * 开启 babel缓存
               * 第二次构建时,会读取缓存,提升构建速度
               */
              cacheDirectory: true
            },
          },

          // 处理图片资源
          {
            test: /\.(png|gif|jpg|jpeg)$/,
            loader: 'url-loader',
            options: {
              // 对于图片小于 8-12kb的可以进行 base64处理
              limit: 8 * 1024,
              // 关闭 ES6解析,使用 commonjs解析
              esModule: false,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
            },
          },
          // 处理 HTML中的 img资源
          {
            test: /\.html$/,
            // 处理 HTML中的 img资源
            loader: 'html-loader',
          },
          // 处理其它资源
          {
            exclude: /\.(html|js|css|less|png|gif|jpg|jpeg)/,
            // 将其它资源原封不动的输出
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]',
              outputPath: 'media',
            },
          },
        ],
      }
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // html代码压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      // 对提取出的 css代码放到统一的文件中,并命名
      filename: 'css/built.[contenthash:8].css',
    }),
    // css代码压缩
    new OptimizeCssAssetsWebpackPlugin(),
  ],
  // mode: 'production',
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3030,
    open: true,
    // 开启 HRM功能
    hot: true,
  },
  /**
   * source-map的设置:
   * [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
   * source-map:内联
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * inline-source-map:内联
   *  1.将生成的 .map文件放在 .js文件中
   *  2.只生成一个内联的 source-map文件
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * hidden-source-map(防止源代码泄露):外联
   *  1.将生成的一个 source-map文件放在单独的 .map文件中
   *  错误提示:错误代码错误原因,但是没有错误位置,
   *  不能追踪源代码错误,只能提示到构建后代码的错误位置
   * eval-source-map:内联
   *  1.针对每一个文件都生成一个单独的 source-map文件,放在 .js文件的 eval函数中
   *  错误提示:错误代码准确信息 和 源代码的错误位置
   * nosource-source-map(防止源代码泄露):外联
   *  错误提示:错误代码准确信息, 但是没有任何源代码信息(可以防止源代码泄露)
   * cheap-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,只能精确到行
   * cheap-module-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,
   *  module会将 loader的 source-map加入
   * 
   */
  // 开启构建后代码与源码的映射
  devtool: 'eval-source-map' // development
  // devtool: 'source-map' // production
};

  • 编写 node服务
// server.js
/**
 * 服务器代码:查看缓存
 * 启动服务器指令:
 *  npm i nodemon -g
 *  nodemon server.js 或
 *  node server.js
 * 访问服务器地址:
 *  http://localhost:3080
 */
// 使用express搭建
const express = require('express')
// 1.创建 express对象
const app = express()
// 2.使用中间件,暴露 'build'文件
app.use(express.static('build', { maxAge: 1000 * 3600 }))
// 3.启动此服务器
app.listen(3080)
  • 运行指令:npx webpack
  • 开启nodejs服务:node server.js

tree shaking:去除应用中无用代码 -24

去除在项目中只引入而没有使用的源代码、下载的第三方库

  • 作用:减少代码体积

前提条件:

  1. 必须使用 ES6模块化
  2. 开启 'production'环境

问题:

  • 在 package.json中配置 "sideEffects": false 所有代码都没有副作用(都可以进行 tree shaking)
    • 可能会将 css、@babel/polyfill等文件忽略,并干掉
    • 应作如下配置:(避免有些文件被干掉)
    // package.json
    "sideEffects": [
     "*.css",
     "*.less"
    ]
    

code split

方式一:(不推荐)

将应用改为 多入口文件

  // 多入口 -> 多页面应用
  // 根据入口的个数最终输出 bundle的个数
  entry: {
    index: './src/js/index.js',
    test: './src/js/test.js'
  },
方式二:使用 optimization的 splitChunks属性
  1. 将 node_modules中代码单独打包成一个 chunk最终输出
  2. 保证多处引入同一个第三方模块只被打包一次
  /**
   * code split:代码拆分
   * 方式二:splitChunks
   * 作用:
   *  1.将 node_modules中代码单独打包成一个 chunk最终输出
   *  2.保证多处引入同一个第三方模块只被打包一次
   */
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },

方式三:通过 js代码 import语法,使得引入的文件被单独打包成一个 chunk
  • /* webpackChunkName: 'test' */:用于命名被打包后的文件
// index.js
/**
  * code split:代码拆分
  * 方式三:通过 js代码,使得引入的文件被单独打包成一个 chunk
 */
// /* webpackChunkName: 'test' */:用于命名被打包后的文件
import(/* webpackChunkName: 'test' */'./test')
  .then((res) => {
    console.log('test', res)
  })
  .catch(() => {
    console.log('文件加载失败!')
  })

总结:

在实际开发中一般通过,方式二和方式三 结合的方式完成代码分割

  • 修改配置文件
// nodejs
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 将 css从 js中提取出来,并到单独的文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// css代码压缩
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

// 定义 nodejs环境变量:决定使用 browserslist的哪个环境
// process.env.NODE_ENV = 'production'
// process.env.NODE_ENV = 'development'

// loader复用
const commonCSSLoader = [
  // 'style-loader',
  // 将 css代码提取到一个指定的文件中
  MiniCssExtractPlugin.loader,
  /**
   * 问题:
   * MiniCssExtractPlugin.loader当前不能将 less文件单独提取出来放到指定的文件中
   */
  // 将 css加载到 js中
  'css-loader',
  /**
   * css兼容性处理:只能针对 css代码来操作
   * postcss ->
   * postcss-loader ->
   * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
   */
  {
    /**
     * 需要在 package.json中定义 browserslist里面的配置
     */
    loader: 'postcss-loader',
    options: {
      // 告诉它要做哪些兼容性配置
      ident: 'postcss',
      plugins: () => [
        /**
         * postcss的插件
         * 帮助 postcss找到 package.json中 browserslist里面的配置,
         * 通过配置加载指定的 css兼容性样式
         */
        require('postcss-preset-env')(),
      ],
    },
  },
];

// commonjs
module.exports = {
  // 单入口 -> 单页面应用
  entry: './src/js/index.js',
  /**
   * code split:代码拆分
   * 方式一:修改入口文件:(不推荐)
   */
  // 多入口 -> 多页面应用
  // 根据入口的个数最终输出 bundle的个数
  // entry: {
  //   index: './src/js/index.js',
  //   test: './src/js/test.js'
  // },
  output: {
    /**
     * 给文件添加 hash值,防止缓存:[hash:8]
     */
    filename: 'js/[name].[contenthash:8].js',
    path: resolve(__dirname, 'build'),
  },
  /**
   * code split:代码拆分
   * 方式二:splitChunks
   * 作用:
   *  1.将 node_modules中代码单独打包成一个 chunk最终输出
   *  2.保证多处引入同一个第三方模块只被打包一次
   */
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  module: {
    rules: [
      // {
      //   /**
      //    * 只检查源代码,忽略第三发库
      //    * eslint-loader
      //    * eslint
      //    * 在 package.json的 eslintConfig中设置检查规则,推荐 airbnb
      //    */
      //   test: /\.js$/,
      //   exclude: /node_modules/,
      //   // 优先执行此 loader
      //   enforce: 'pre',
      //   loader: 'eslint-loader',
      //   options: {
      //     // 自动修复 eslint错误
      //     fix: true,
      //   },
      // },
      {
        /**
         * oneOf:提升打包构建速度,阻止每种类型的文件都被以下 loader判断一次
         * 在 oneOf中的 loader只会匹配一个
         * 注意:在 oneOf中不能有两个及以上的配置处理同一种类型的文件
         *    如果有,需要拿到 oneOf外边处理
         */
        oneOf: [
          // css代码处理
          {
            test: /\.css$/,
            use: [
              ...commonCSSLoader,
              /**
               * 
               */
              // // 将 css代码提取到一个指定的文件中
              // MiniCssExtractPlugin.loader,
              // // 将 css加载到 js中
              // 'css-loader',
              // /**
              //  * css兼容性处理:只能针对 css代码来操作
              //  * postcss ->
              //  * postcss-loader ->
              //  * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
              //  */
              // {
              //   /**
              //    * 需要在 package.json中定义 browserslist里面的配置
              //    */
              //   loader: 'postcss-loader',
              //   options: {
              //     // 告诉它要做哪些兼容性配置
              //     ident: 'postcss',
              //     plugins: () => [
              //       /**
              //        * postcss的插件
              //        * 帮助 postcss找到 package.json中 browserslist里面的配置,
              //        * 通过配置加载指定的 css兼容性样式
              //        */
              //       require('postcss-preset-env')()
              //     ]
              //   }
              // }
            ],
          },
          {
            test: /\.less$/,
            use: [
              ...commonCSSLoader,
              // MiniCssExtractPlugin.loader,
              // 'css-loader',
              'less-loader',
            ],
          },
          /**
           * 正常来讲,一个文件只能被一个 loader处理;
           * 当一个文件要被多个 loader处理,那一定要指定 loader执行的先后顺序
           * 如下面的 .js文件:应该先执行 eslint再执行 bable,通过 enforce属性指定
           */
          // js语法检查:规范项目代码,检查常见语法错误

          {
            // js兼容性处理:babel-loader、@babel/preset-env、@babel/core
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              // 预设:指示 babel做怎样的兼容处理
              presets: [
                [
                  // 只能作简单的语法兼容处理
                  '@babel/preset-env',
                  // corejs可以作复杂的语法兼容处理
                  {
                    // 按需加载
                    useBuiltIns: 'usage',
                    // 指定 core-js版本
                    corejs: {
                      version: 3,
                    },
                    // 指定兼容性做到哪个版本的浏览器
                    targets: {
                      chrome: '60',
                      firefox: '50',
                      safari: '10',
                      edge: '17',
                      ie: '9',
                    },
                  },
                ],
              ],
              /**
               * 开启 babel缓存
               * 第二次构建时,会读取缓存,提升构建速度
               */
              cacheDirectory: true
            },
          },

          // 处理图片资源
          {
            test: /\.(png|gif|jpg|jpeg)$/,
            loader: 'url-loader',
            options: {
              // 对于图片小于 8-12kb的可以进行 base64处理
              limit: 8 * 1024,
              // 关闭 ES6解析,使用 commonjs解析
              esModule: false,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
            },
          },
          // 处理 HTML中的 img资源
          {
            test: /\.html$/,
            // 处理 HTML中的 img资源
            loader: 'html-loader',
          },
          // 处理其它资源
          {
            exclude: /\.(html|js|css|less|png|gif|jpg|jpeg)/,
            // 将其它资源原封不动的输出
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]',
              outputPath: 'media',
            },
          },
        ],
      }
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // html代码压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      // 对提取出的 css代码放到统一的文件中,并命名
      filename: 'css/built.[contenthash:8].css',
    }),
    // css代码压缩
    new OptimizeCssAssetsWebpackPlugin(),
  ],
  mode: 'production',
  // mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3030,
    open: true,
    // 开启 HRM功能
    hot: true,
  },
  /**
   * source-map的设置:
   * [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
   * source-map:内联
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * inline-source-map:内联
   *  1.将生成的 .map文件放在 .js文件中
   *  2.只生成一个内联的 source-map文件
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * hidden-source-map(防止源代码泄露):外联
   *  1.将生成的一个 source-map文件放在单独的 .map文件中
   *  错误提示:错误代码错误原因,但是没有错误位置,
   *  不能追踪源代码错误,只能提示到构建后代码的错误位置
   * eval-source-map:内联
   *  1.针对每一个文件都生成一个单独的 source-map文件,放在 .js文件的 eval函数中
   *  错误提示:错误代码准确信息 和 源代码的错误位置
   * nosource-source-map(防止源代码泄露):外联
   *  错误提示:错误代码准确信息, 但是没有任何源代码信息(可以防止源代码泄露)
   * cheap-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,只能精确到行
   * cheap-module-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,
   *  module会将 loader的 source-map加入
   * 
   */
  // 开启构建后代码与源码的映射
  devtool: 'eval-source-map' // development
  // devtool: 'source-map' // production
};

  • index.js
import $ from 'jquery';
import print from './print';
// import { mul } from './test';

import '../css/iconfont.css';
import '../css/a.css';
import '../css/b.less';

/**
  * code split:代码拆分
  * 方式三:通过 js代码,使得引入的文件被单独打包成一个 chunk
 */
// /* webpackChunkName: 'test' */:用于命名被打包后的文件
import(/* webpackChunkName: 'test' */'./test')
  .then((res) => {
    console.log('test', res)
  })
  .catch(() => {
    console.log('文件加载失败!')
  })

const add = (x, y) => x + y;
console.log(add(1, 4));
// console.log(res.mul(1, 2));

if (module.hot) {
  // 一旦 module.hot为 true,说明开启了 HMR功能 -> 让 HMR功能代码生效
  module.hot.accept('./print.js', () => {
    /**
     * 方法会监听 print.js文件变化,一旦发生变化,其它模块不会重新打包
     * 会执行后面的回调函数
     */
    print();
  });
}
console.log($);

  • 运行指令:npx webpack

lazy loading:js文件懒加载 -26

利用代码分割的思路,将要被懒加载的 文件放到异步回调函数中,通过代码分割 import函数引入文件,当满足异步函数触发条件时加载对应的文件

注意:实现 懒加载的前提是先进行 代码分割

  • 正常加载:并行加载(同一时间加载多个文件)
    • http协议,同一个域名只能同时加载六个文件
  • 懒加载:当文件被使用时才加载
  • 预加载 prefetch(不推荐):在使用之前,提前加载 js文件 webpackPrefetch: true
    • 等其它资源加载完毕,浏览器再偷偷加载 预加载资源
    • 只在 pc端可用,ie和移动端有兼容问题
// index.js
/**
 * lazy loading
 */
document.getElementById('btn').onclick = function () {
  // 此时 .test就会变成懒加载
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test')
    .then(({ mul }) => {
      console.log(mul(1, 3))
    })
}

pwa:渐进式网络开发应用程序(离线可访问)

使得网页可以像 app一样,可以离线访问,性能也更好

配置:

  1. 在 webpack.config.js中下载插件:workbox-webpack-plugin
    plugins: [
      /**
      * pwa:
      * 1.帮助 serviceworker快速启动
      * 2.删除旧的 serviceworker
      * 最终:生成一个 serviceworker配置文件
      * 然后在入口文件中注册 serviceworker
      */
        new WorkboxWebpackPlugin.GenerateSW({
          clientsClaim: true,
          skipWaiting: true
        })
    ]
    
  2. 在入口文件中 index.js注册 serviceWorker
/**
 * 注册 serviceworker:
 * 处理兼容性问题
 */
/**
 * 问题:
 * 1.eslint不认识全局变量:window、navigator...
 * 需要设置 eslintConfig的 env属性
 *
 * 2.serviceworker代码必须运行在服务器上
 *  -1. nodejs
 *  -2. npm i serve -g,快速创建一个服务器
 *   运行 serve -s build,启动服务器,将 build目录下所有资源作为静态资源暴露出去
 */
if ('serviceWorker' in navigator) {
  // 全部资源加载完成后
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
      .then(() => {
        console.log('sw success');
      })
      .catch(() => {
        console.log('sw fail');
      });
  });
}

  1. 解决一些问题:
  • eslint
 /*
 1.eslint不认识全局变量:window、navigator...
  需要设置 eslintConfig的 env属性
 */
  "eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  },
  • 开启服务器
// 2.开启服务器
 /* 2.serviceworker代码必须运行在服务器上
 *  -1. nodejs
 *  -2. npm i serve -g,快速创建一个服务器
 *   运行 serve -s build,启动服务器,将 build目录下所有资源作为静态资源暴露出去
 */
  • 修改配置文件
// nodejs
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 将 css从 js中提取出来,并到单独的文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// css代码压缩
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// pwa
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')

// 定义 nodejs环境变量:决定使用 browserslist的哪个环境
// process.env.NODE_ENV = 'production'
// process.env.NODE_ENV = 'development'

// loader复用
const commonCSSLoader = [
  // 'style-loader',
  // 将 css代码提取到一个指定的文件中
  MiniCssExtractPlugin.loader,
  /**
   * 问题:
   * MiniCssExtractPlugin.loader当前不能将 less文件单独提取出来放到指定的文件中
   */
  // 将 css加载到 js中
  'css-loader',
  /**
   * css兼容性处理:只能针对 css代码来操作
   * postcss ->
   * postcss-loader ->
   * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
   */
  {
    /**
     * 需要在 package.json中定义 browserslist里面的配置
     */
    loader: 'postcss-loader',
    options: {
      // 告诉它要做哪些兼容性配置
      ident: 'postcss',
      plugins: () => [
        /**
         * postcss的插件
         * 帮助 postcss找到 package.json中 browserslist里面的配置,
         * 通过配置加载指定的 css兼容性样式
         */
        require('postcss-preset-env')(),
      ],
    },
  },
];

// commonjs
module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    /**
     * 给文件添加 hash值,防止缓存:[hash:8]
     */
    filename: 'js/built.[contenthash:8].js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      {
        /**
         * 只检查源代码,忽略第三发库
         * eslint-loader
         * eslint
         * 在 package.json的 eslintConfig中设置检查规则,推荐 airbnb
         */
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行此 loader
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          // 自动修复 eslint错误
          fix: true,
        },
      },
      {
        /**
         * oneOf:提升打包构建速度,阻止每种类型的文件都被以下 loader判断一次
         * 在 oneOf中的 loader只会匹配一个
         * 注意:在 oneOf中不能有两个及以上的配置处理同一种类型的文件
         *    如果有,需要拿到 oneOf外边处理
         */
        oneOf: [
          // css代码处理
          {
            test: /\.css$/,
            use: [
              ...commonCSSLoader,
              /**
               * 
               */
              // // 将 css代码提取到一个指定的文件中
              // MiniCssExtractPlugin.loader,
              // // 将 css加载到 js中
              // 'css-loader',
              // /**
              //  * css兼容性处理:只能针对 css代码来操作
              //  * postcss ->
              //  * postcss-loader ->
              //  * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
              //  */
              // {
              //   /**
              //    * 需要在 package.json中定义 browserslist里面的配置
              //    */
              //   loader: 'postcss-loader',
              //   options: {
              //     // 告诉它要做哪些兼容性配置
              //     ident: 'postcss',
              //     plugins: () => [
              //       /**
              //        * postcss的插件
              //        * 帮助 postcss找到 package.json中 browserslist里面的配置,
              //        * 通过配置加载指定的 css兼容性样式
              //        */
              //       require('postcss-preset-env')()
              //     ]
              //   }
              // }
            ],
          },
          {
            test: /\.less$/,
            use: [
              ...commonCSSLoader,
              // MiniCssExtractPlugin.loader,
              // 'css-loader',
              'less-loader',
            ],
          },
          /**
           * 正常来讲,一个文件只能被一个 loader处理;
           * 当一个文件要被多个 loader处理,那一定要指定 loader执行的先后顺序
           * 如下面的 .js文件:应该先执行 eslint再执行 bable,通过 enforce属性指定
           */
          // js语法检查:规范项目代码,检查常见语法错误

          {
            // js兼容性处理:babel-loader、@babel/preset-env、@babel/core
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              // 预设:指示 babel做怎样的兼容处理
              presets: [
                [
                  // 只能作简单的语法兼容处理
                  '@babel/preset-env',
                  // corejs可以作复杂的语法兼容处理
                  {
                    // 按需加载
                    useBuiltIns: 'usage',
                    // 指定 core-js版本
                    corejs: {
                      version: 3,
                    },
                    // 指定兼容性做到哪个版本的浏览器
                    targets: {
                      chrome: '60',
                      firefox: '50',
                      safari: '10',
                      edge: '17',
                      ie: '9',
                    },
                  },
                ],
              ],
              /**
               * 开启 babel缓存
               * 第二次构建时,会读取缓存,提升构建速度
               */
              cacheDirectory: true
            },
          },

          // 处理图片资源
          {
            test: /\.(png|gif|jpg|jpeg)$/,
            loader: 'url-loader',
            options: {
              // 对于图片小于 8-12kb的可以进行 base64处理
              limit: 8 * 1024,
              // 关闭 ES6解析,使用 commonjs解析
              esModule: false,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
            },
          },
          // 处理 HTML中的 img资源
          {
            test: /\.html$/,
            // 处理 HTML中的 img资源
            loader: 'html-loader',
          },
          // 处理其它资源
          {
            exclude: /\.(html|js|css|less|png|gif|jpg|jpeg)/,
            // 将其它资源原封不动的输出
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]',
              outputPath: 'media',
            },
          },
        ],
      }
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // html代码压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      // 对提取出的 css代码放到统一的文件中,并命名
      filename: 'css/built.[contenthash:8].css',
    }),
    // css代码压缩
    new OptimizeCssAssetsWebpackPlugin(),
    /**
     * pwa:
     * 1.帮助 serviceworker快速启动
     * 2.删除旧的 serviceworker
     * 最终:生成一个 serviceworker配置文件
     * 然后在入口文件中注册 serviceworker
     */
    new WorkboxWebpackPlugin.GenerateSW({
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  mode: 'production',
  // mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3030,
    open: true,
    // 开启 HRM功能
    hot: true,
  },
  /**
   * source-map的设置:
   * [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
   * source-map:内联
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * inline-source-map:内联
   *  1.将生成的 .map文件放在 .js文件中
   *  2.只生成一个内联的 source-map文件
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * hidden-source-map(防止源代码泄露):外联
   *  1.将生成的一个 source-map文件放在单独的 .map文件中
   *  错误提示:错误代码错误原因,但是没有错误位置,
   *  不能追踪源代码错误,只能提示到构建后代码的错误位置
   * eval-source-map:内联
   *  1.针对每一个文件都生成一个单独的 source-map文件,放在 .js文件的 eval函数中
   *  错误提示:错误代码准确信息 和 源代码的错误位置
   * nosource-source-map(防止源代码泄露):外联
   *  错误提示:错误代码准确信息, 但是没有任何源代码信息(可以防止源代码泄露)
   * cheap-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,只能精确到行
   * cheap-module-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,
   *  module会将 loader的 source-map加入
   * 
   */
  // 开启构建后代码与源码的映射
  devtool: 'eval-source-map' // development
  // devtool: 'source-map' // production
};

  • index.js入口文件
import print from './print';
import { mul } from './test';

import '../css/iconfont.css';
import '../css/a.css';
import '../css/b.less';

const add = (x, y) => x + y;
console.log(add(1, 4));
console.log(mul(1, 2));

if (module.hot) {
  // 一旦 module.hot为 true,说明开启了 HMR功能 -> 让 HMR功能代码生效
  module.hot.accept('./print.js', () => {
    /**
     * 方法会监听 print.js文件变化,一旦发生变化,其它模块不会重新打包
     * 会执行后面的回调函数
     */
    print();
  });
}
/**
 * 注册 serviceworker:
 * 处理兼容性问题
 */
/**
 * 问题:
 * 1.eslint不认识全局变量:window、navigator...
 * 需要设置 eslintConfig的 env属性
 *
 * 2.serviceworker代码必须运行在服务器上
 *  -1. nodejs
 *  -2. npm i serve -g,快速创建一个服务器
 *   运行 serve -s build,启动服务器,将 build目录下所有资源作为静态资源暴露出去
 */
if ('serviceWorker' in navigator) {
  // 全部资源加载完成后
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
      .then(() => {
        console.log('sw success');
      })
      .catch(() => {
        console.log('sw fail');
      });
  });
}

多进程打包

主要是用于 babel-loader,开启多进程打包

问题:

  1. 进程启动大概需要 600ms,另外进程通信也有开销
  2. 只有工作消耗时间较长,才需要多进程打包

配置

  • 下载安装包
npm install --save-dev thread-loader
  • 修改配置文件
// nodejs
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 将 css从 js中提取出来,并到单独的文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// css代码压缩
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// pwa
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')

// 定义 nodejs环境变量:决定使用 browserslist的哪个环境
// process.env.NODE_ENV = 'production'
// process.env.NODE_ENV = 'development'

// loader复用
const commonCSSLoader = [
  // 'style-loader',
  // 将 css代码提取到一个指定的文件中
  MiniCssExtractPlugin.loader,
  /**
   * 问题:
   * MiniCssExtractPlugin.loader当前不能将 less文件单独提取出来放到指定的文件中
   */
  // 将 css加载到 js中
  'css-loader',
  /**
   * css兼容性处理:只能针对 css代码来操作
   * postcss ->
   * postcss-loader ->
   * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
   */
  {
    /**
     * 需要在 package.json中定义 browserslist里面的配置
     */
    loader: 'postcss-loader',
    options: {
      // 告诉它要做哪些兼容性配置
      ident: 'postcss',
      plugins: () => [
        /**
         * postcss的插件
         * 帮助 postcss找到 package.json中 browserslist里面的配置,
         * 通过配置加载指定的 css兼容性样式
         */
        require('postcss-preset-env')(),
      ],
    },
  },
];

// commonjs
module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    /**
     * 给文件添加 hash值,防止缓存:[hash:8]
     */
    filename: 'js/built.[contenthash:8].js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      {
        /**
         * 只检查源代码,忽略第三发库
         * eslint-loader
         * eslint
         * 在 package.json的 eslintConfig中设置检查规则,推荐 airbnb
         */
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行此 loader
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          // 自动修复 eslint错误
          fix: true,
        },
      },
      {
        /**
         * oneOf:提升打包构建速度,阻止每种类型的文件都被以下 loader判断一次
         * 在 oneOf中的 loader只会匹配一个
         * 注意:在 oneOf中不能有两个及以上的配置处理同一种类型的文件
         *    如果有,需要拿到 oneOf外边处理
         */
        oneOf: [
          // css代码处理
          {
            test: /\.css$/,
            use: [
              ...commonCSSLoader,
              /**
               * 
               */
              // // 将 css代码提取到一个指定的文件中
              // MiniCssExtractPlugin.loader,
              // // 将 css加载到 js中
              // 'css-loader',
              // /**
              //  * css兼容性处理:只能针对 css代码来操作
              //  * postcss ->
              //  * postcss-loader ->
              //  * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
              //  */
              // {
              //   /**
              //    * 需要在 package.json中定义 browserslist里面的配置
              //    */
              //   loader: 'postcss-loader',
              //   options: {
              //     // 告诉它要做哪些兼容性配置
              //     ident: 'postcss',
              //     plugins: () => [
              //       /**
              //        * postcss的插件
              //        * 帮助 postcss找到 package.json中 browserslist里面的配置,
              //        * 通过配置加载指定的 css兼容性样式
              //        */
              //       require('postcss-preset-env')()
              //     ]
              //   }
              // }
            ],
          },
          {
            test: /\.less$/,
            use: [
              ...commonCSSLoader,
              // MiniCssExtractPlugin.loader,
              // 'css-loader',
              'less-loader',
            ],
          },
          /**
           * 正常来讲,一个文件只能被一个 loader处理;
           * 当一个文件要被多个 loader处理,那一定要指定 loader执行的先后顺序
           * 如下面的 .js文件:应该先执行 eslint再执行 bable,通过 enforce属性指定
           */
          // js语法检查:规范项目代码,检查常见语法错误

          {
            // js兼容性处理:babel-loader、@babel/preset-env、@babel/core
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              /**
               * 开启多进程打包:
               */
              {
                loader: 'thread-loader',
                options: {
                  workers: 2 // 规定只开启两个进程,默认是 cpu核数 - 1
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  // 预设:指示 babel做怎样的兼容处理
                  presets: [
                    [
                      // 只能作简单的语法兼容处理
                      '@babel/preset-env',
                      // corejs可以作复杂的语法兼容处理
                      {
                        // 按需加载
                        useBuiltIns: 'usage',
                        // 指定 core-js版本
                        corejs: {
                          version: 3,
                        },
                        // 指定兼容性做到哪个版本的浏览器
                        targets: {
                          chrome: '60',
                          firefox: '50',
                          safari: '10',
                          edge: '17',
                          ie: '9',
                        },
                      },
                    ],
                  ],
                  /**
                   * 开启 babel缓存
                   * 第二次构建时,会读取缓存,提升构建速度
                   */
                  cacheDirectory: true
                },
              }
            ],
          },

          // 处理图片资源
          {
            test: /\.(png|gif|jpg|jpeg)$/,
            loader: 'url-loader',
            options: {
              // 对于图片小于 8-12kb的可以进行 base64处理
              limit: 8 * 1024,
              // 关闭 ES6解析,使用 commonjs解析
              esModule: false,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
            },
          },
          // 处理 HTML中的 img资源
          {
            test: /\.html$/,
            // 处理 HTML中的 img资源
            loader: 'html-loader',
          },
          // 处理其它资源
          {
            exclude: /\.(html|js|css|less|png|gif|jpg|jpeg)/,
            // 将其它资源原封不动的输出
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]',
              outputPath: 'media',
            },
          },
        ],
      }
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // html代码压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      // 对提取出的 css代码放到统一的文件中,并命名
      filename: 'css/built.[contenthash:8].css',
    }),
    // css代码压缩
    new OptimizeCssAssetsWebpackPlugin(),
    /**
     * pwa:
     * 1.帮助 serviceworker快速启动
     * 2.删除旧的 serviceworker
     * 最终:生成一个 serviceworker配置文件
     * 然后在入口文件中注册 serviceworker
     */
    // new WorkboxWebpackPlugin.GenerateSW({
    //   clientsClaim: true,
    //   skipWaiting: true
    // })
  ],
  mode: 'production',
  // mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3030,
    open: true,
    // 开启 HRM功能
    hot: true,
  },
  /**
   * source-map的设置:
   * [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
   * source-map:内联
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * inline-source-map:内联
   *  1.将生成的 .map文件放在 .js文件中
   *  2.只生成一个内联的 source-map文件
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * hidden-source-map(防止源代码泄露):外联
   *  1.将生成的一个 source-map文件放在单独的 .map文件中
   *  错误提示:错误代码错误原因,但是没有错误位置,
   *  不能追踪源代码错误,只能提示到构建后代码的错误位置
   * eval-source-map:内联
   *  1.针对每一个文件都生成一个单独的 source-map文件,放在 .js文件的 eval函数中
   *  错误提示:错误代码准确信息 和 源代码的错误位置
   * nosource-source-map(防止源代码泄露):外联
   *  错误提示:错误代码准确信息, 但是没有任何源代码信息(可以防止源代码泄露)
   * cheap-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,只能精确到行
   * cheap-module-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,
   *  module会将 loader的 source-map加入
   * 
   */
  // 开启构建后代码与源码的映射
  devtool: 'eval-source-map' // development
  // devtool: 'source-map' // production
};

  • 运行指令:webpack

externals

防止将一些第三方包,打包到最终的输出中 bundles,如 jQuery,我们希望通过 cdn引入进来,而不参与打包

  • 修改配置文件
// webpack.config.js
  mode: 'production',
  // mode: 'development',
  /**
   * externals:
   * 添加不被打包的第三方库
   */
  externals: {
    // 忽略的库名 -> npm包名
    jquery: 'jQuery'
  },
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3030,
    open: true,
    // 开启 HRM功能
    hot: true,
  },

dll

webpack 基础_优化2