Skip to content

一 注意

1. webpack版本问题

  • 由于教程使用的是webpack4的版本,所以也建议使用webpack4的版本,比较好兼容,尤其是更新到webpack5以后,有很多兼容性的问题,例如html打包插件不兼容,devServer调用方式也有变化,css单独打包的插件也会出现不兼容。

2. 特别注意

  • webpack5 使用 html-webpack-plugin 时会报错 The 'compilation' argument must be an instance of Compilation,解决办法是把该插件的安装方法从 npm i html-webpack-plugin -D 改为 npm i html-webpack-plugin@next -D

3. webpack5 个核心概念

  • entry
  • output
  • loader
  • plugins
  • mode
Loader 和 Plugin 的区别
  1. loader 是一个转换器,将 a 文件进行编译输出 b 文件,这里是操作文件。单纯的文件转换。
  2. plugin 是一个扩展器,它丰富了 webpack 本身,针对是 loader 结束后,webpack 打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听 webpack 打包过程中的某些节点,执行任务

webpack初体验

1. webpack初体验

  • 运行指令: 开发环境webpack ./src/index.js -o ./build --mode=development 生产环境webpack ./src/index.js -o ./build --mode=production

  • 结论: webpack 只能处理 js/json 文件,不能处理 css/img 等其他资源 生产环境比开发环境多一个压缩 js 代码

2. 打包样式资源

打包样式资源,需要使用 webpack.config.js,是 webpack 的配置文件,在其中可以配置调用的loaderplugins, 并且需要在module中配置rules

  • 打包css资源使用style-loadercss-loader,使用之前需要使用npm进行安装: npm i style-loader css-loader -D,并在`webpack.config.js 中进行配置。

  • 打包less资源使用style-loadercss-loaderless-loader,使用之前需要使用npm进行安装:(其中less-loader又需要依赖less模块) npm i less less-loader -D,并在`webpack.config.js 中进行配置。

特别的,在path中需要使用绝对地址,nodejs提供了接口,使用const { resolve } = require('path')调用,随后在path中调用 resolve(__dirname, "build")

webpack.config.js配置

javascript
const { resolve } = require('path') // 用于定义当前文件的绝对路径

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    // loader的配置
    rules: [
      {
        // test 用于匹配哪些文件
        test: /\.css$/,
        // 使用哪些loader 进行处理
        use: [
          // 创建style标签,再将js中的样式资源插入,添加到head中生效
          'style-loader',
          // 将css文件变成commonjs模块加载js中,内容样式是字符串
          'css-loader'
          // 注意:use调用的loader,顺序是从下往上/从右往左
        ]
      },
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      }
    ]
  },

  plugins: [],

  mode: 'development'
}

3. 打包html资源

打包html文件则需要使用html-webpack-pluginplugins,但pluginsloader的使用有一些不同:loader的使用需要经过 (1)下载->(2)使用,而plugins的使用则是 (1)下载->(2)调用->(3)使用

  • 具体使用html-webpack-plugin: npm i html-webpack-plugin -D 进行安装。之后在webpack.config.js中进行引用,const HtmlWebPackPlugin = require('html-webpack-plugin')

webpack.config.js配置

javascript
const HtmlWebPackPlugin = require('html-webpack-plugin');// 调用html-webpack-plugin插件
module.exports = {

  plugins: [
    // plugins 的配置
    // 打包html资源需要 html-webpack-plugin 的包。
    // 功能:默认会创建一个空的html文件,引入打包输出的所有资源(包括js/css),但是此时的html文件。结构(包括 title 等没有自定义)
    // 现在的需求:需要一个有结构的html文件,可以使用template定义一个文件
    new HtmlWebPackPlugin({
      // 意思是 复制 ./src/index.html 文件,并自动打包输出所有的资源文件(js/css)
      template: './src/index.html'
    })
  ],

  mode: 'development'

4. 打包图片资源 url-loader file-loader

  • 打包css中的图片资源使用url-loader使用之前需要使用npm进行安装:(其中url-loader又需要依赖file-loader模块) npm i url-loader file-loader -D,并在`webpack.config.js 中进行配置。

  • 除了css中的图片需要打包,还有html页面中的图片需要打包,html-loader 专门用于处理 html 文件中的图片,主要是负责引入html中的img,从而能够被url-loader进行处理。

webpack.config.js配置

javascript
const { resolve } = require("path"); // 获取绝对地址
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {

    rules: [
      {
        test: /\.less$/,
        // 需要使用多个loader,则使用use
        use: ["style-loader", "css-loader", "less-loader"],
      },
      {
        // 这种方式只能处理样式中的图片,不能处理html中的img图片
        test: /\.(jpg|png|gif)$/,
        // 如果只有一个loader,就直接使用loader
        loader: "url-loader",
        // url-loader 需要依赖file-loader 和url-loader
        // url-loader 可以将较小的图片转换为base64位图片,减少服务器的请求
        options: {
          // limit中的8 指图片小于8K,就会被base64处理
          // base64的优点:减少请求的数量
          // 缺点:图片体积会增大
          limit: 8 * 1024,
          // [name]取文件原本的名字
          // [hash:10]取图片的hash值的前10位
          // [ext]取文件原来的扩展名
          name: "[name].[hash:10].[ext]",
        },
      },
      {
        test: /\.html$/,
        // html-loader 专门用于处理html文件中的图片
        loader: "html-loader",
      },
    ],

  },

};

5. 打包其他资源

  • 打包其他资源,就是除了样式、js,图片等的其他资源。使用file-loader进行打包。在之前的处理图片资源中,已经使用npm下载过。

webpack.config.js配置

javascript
const { resolve } = require('path')
module.exports = {
  module: {
    rules: [
      // 打包其他资源 可以使用test指定文件类型,也可以使用exclude排除
      {
        exclude: /\.(css|js|html)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[hash:10].[ext]',
          outputPath: 'other' // 在outputPath 中配置其他资源的输出目录
        }
      }
    ]
  }
}

6. devServer 开发服务器

devServerwebpack提供的一个本地服务器,用于自动化,包括编译,打包,打开浏览器,自动刷新浏览器。并且只会在内存中编译打包,不会有具体的输出。

  • 使用devServer需要进行安装 npm i webpack-dev-server -D 启动devServer的指令为: webpack4:npx webpack-dev-serverwebpack5:npx webpack serve

    webpack.config.js配置

javascript
const { resolve } = require('path')
module.exports = {
  devServer: {
    // 运行项目的目录
    contentBase: resolve(__dirname, 'bulid'),
    // 启动gzip压缩
    compress: true,
    // 端口号
    port: 5050,
    // 自动打开浏览器
    open: true
  }
}

然后使用npm run webpack serve 启动服务,另,也可以不对webpack.config.js进行配置,直接启动webpack serve服务。

7. 开发环境的搭建

开发环境就是包括之前的图片、html、其他资源的打包配置,并调用devServer,为了将打包的资源输出到对应目录,需要在options中配置 outputPath,但是入口文件只需在output中配置filename,另外css类型的文件转换为js文件,所以此时没有额外输出css文件。

webpack.config.js配置

javascript
const HtmlWebPackPlugin = require('html-webpack-plugin')
const { resolve } = require('path')

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/build.js', // 增加 js/build.js js会输出到js目录中
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // 处理css资源
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      // 处理less资源
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      // 处理图片资源
      {
        test: /\.(jpg|png|gif|svg)$/,
        loader: 'url-loader',
        options: {
          name: '[name].[hash:10].[ext]',
          limit: 8 * 1024,
          outputPath: 'imgs' // 在outputPath 中配置其他资源的输出目录
        }
      },
      // 处理html中的图片资源
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      // 处理其他资源
      {
        exclude: /\.(css|js|less|jpg|png|gif|html|svg)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[hash:10].[ext]',
          outputPath: 'other' // 在outputPath 中配置其他资源的输出目录
        }
      }
    ]
  },
  plugins: [
    // 处理html资源
    new HtmlWebPackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'bulid'),
    compress: true,
    port: 5050,
    open: true
  }
}

webpack

1. 提取 css 成为一个单独文件 mini-css-extract-plugin

  • 打包时提取css成为一个单独文件,使用mini-css-extract-plugin进行实现,webpack5暂时还不兼容

  • 使用 npm i mini-css-extract-plugin -D 进行安装,然后再 plugins 中配置: new MiniCssExtractPlugin({}),以及 loader 中的配置:MiniCssExtractPlugin.loader

webpack.config.js配置

javascript
// 用于将css文件单独提取成一个文件的插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 'style-loader',
          // 用MiniCssExtractPlugin.loader取代style-loader ,不用将css文件插入head中,而是引用一个单独的文件

          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    // plugins 中进行配置
    new MiniCssExtractPlugin({
      // 对输出的css文件重命名
      filename: 'css/[hash:5].css'
    })
  ]
}

2. 处理 css 文件的兼容性 postcss

  • css 兼容性处理使用 postcss ,需要安装两个 loader ,分别是 postcss-loaderpostcss-preset-envpostcss 是帮助 webpackpackage.json 中找到 browserslist 中的配置,通过配置加载指定的 css 兼容性。 下面是 package.json 中的配置,其中的生产和开发环境不是指 webpack.config.js 中配置的环境,是指 nodejs 的环境,所以需要修改 nodejs 的环境为开发环境。修改的方式为: process.env.NODE_ENV = 'development'

  • 使用npm进行安装:npm i postcss-loader postcss-preset-env -D ,安装之后在 loader 中进行配置,

javascript

{
  loader: 'postcss-loader',
  options: {
    postcssOptions: {
      ident: 'postcss',
      plugins: [
        require('postcss-preset-env')()
      ]
    }
  }
}
  • 除了在 webpack.config.js 中进行配置以外,还需要在 package.json 中进行 borwerslist 的配置:
json
  "borwerslist": {
    // 开发环境
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    // 生产环境
    "production": [
      "> 1%",
      "last 2 versions",
      "not ie <= 8"
    ]
  }
  • borwerslist的配置不是固定的,可以有其他的参考,而且当中的生产和开发环境是指 nodejs 环境变量,默认是生产环境的,所以要调整 nodejs 的环境变量还要在 webpack.config.js 中进行配置: process.env.NODE_ENV = 'development'
javascript
process.env.NODE_ENV = 'development'

3. 处理 css 文件的压缩

  • 压缩 css 需要使用 optimize-css-assets-webpack-plugin 插件。

  • 使用 npm i optimize-css-assets-webpack-plugin -D 进行安装,安装之后在 webpack.config.js 中进行配置使用,和之前的插件一样:

javascript
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin"); // 引用插件
...
plugins: [
  new OptimizeCssAssetsWebpackPlugin()
  ];
...

4. js的语法检查

  • js的语法检查使用eslint,依赖于 eslint-loadereslint ,只检查自己写的源代码,第三方库不检查, 使用前还得设置检查规则, 需要在 package.json 中进行配置。 通常使用 Airbnbjs 语法规范,或者是自己使用 eslint 定义。

  1. vscode下载 eslint 插件;
  2. npm i eslint -g全局安装 eslint插件;
  3. 在对应的文件夹建立项目npm init,和 eslint 初始化 eslint --init
  4. 配置 vscodeeslint 的引用地址:
json
  "eslint.options": {
    "configFile": "E:/Git/studyProgram/.eslintrc.js"
  },
  1. 配置 .eslintrc.js 具体配置:
javascript
module.exports = {
  env: {
    browser: true,
    commonjs: true,
    es6: true
  },
  extends: 'eslint:recommended',
  parserOptions: {
    sourceType: 'module'
  },
  rules: {
    indent: [
      'error',
      2,
      {
        SwitchCase: 1
      }
    ],
    'linebreak-style': ['off', 'windows'],
    // 引号
    quotes: ['error', 'single', { avoidEscape: true }],
    // 末尾的分号
    semi: ['error', 'always'],
    // 未定义的变量(例如其他页面调用的变量,在另外的页面中使用)
    'no-undef': [0],
    // 定义了却没有调用的变量
    'no-unused-vars': [
      0
      // { 'vars': 'all', 'args': 'after-used', 'ignoreRestSiblings': false }
    ],
    'no-prototype-builtins': 'error'
  }
}

5. js的兼容性

  • js 兼容性处理使用 babel-loader @babel/core @babel/preset-env进行简单的兼容性处理,只能转换基本的语法,不能转换 Promise等 使用 npm i babel-loader @babel/core @babel/preset-env -D 进行安装,使用时,需要在 webpack.config.js 中进行配置:
javascript

...
{
  test: /\.js$/,
  exclude: /node_modules/,
  loader: 'babel-loader',
  options:{
    presets:['@babel/presets-env']
  }
}
...
  • 使用 @babel/polyfill 可以进行全部的兼容性处理,但是转换之后的文件,体积太大,基本弃用了。 npm i @babel/polyfill -D 安装之后,调用上述的 loader 随后在需要全部兼容的 js 文件中添加 import '@babel/polyfill'
  • exclude 用于排除不需要打包的文件夹
  • 按需处理,根据需要进行浏览器的兼容性处理,需要使用 core-jsnpm i core-js -D 安装后,还需要修改 webpack.config.js的配置,注:presets中,是两个方括号。
javascript

  {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
      // 预设 指示babel做怎么样的兼容性处理
      // presets: ['@babel/preset-env'] // 基础js兼容性处理
      presets: [
        [
          '@babel/preset-env',
          {
            // 进行按需加载
            useBuiltIns: 'usage',
            // corejs 的版本
            corejs: {
              version: 3
            },
            // 兼容性目标
            targets: {
              chrome: '60',
              ie: '9',
              firefox: '60'
            }
          }
        ]
      ]
    }
  }

6. js的压缩

  • 使用 webpack 进行 js 的压缩。只需要把 webpack.config.js 中的模式调为生产模式,就能自动进行 js 的压缩。 mode: 'production'

7. html的压缩

  • webpack@4.41.6 版本中,不会自动进行 html 的压缩,但是在高版本的 webpack 中则是会自动压缩 html 文件的。 压缩 html 文件使用的是 html-webpack-plugin,在 new HtmlWebpackPlugin() 中进行一些配置就可以进行 html 文件的压缩:

    javascript
    ...
      new HtmlWebpackPlugin({
        template: './index.html',
        minify:{
          // 移除空格
          collapseWhitespace:true,
          // 移除注释
          removeComments:true
        }
      }),
    ...

8. 生产环境配置

  • 生产环境的配置,就是包括 css - js - hmtl - 图片 - 其他文件,环境变量为生产环境的打包配置, css 文件不止是 css 文件,还有其它的如: less scss styls 等的处理,不过此处以 less 文件为例。由于内容较多,配置的时候最好能够配置一项测试一项。避免后面不知道哪里有问题。

webpack.config.js配置

javascript
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')

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    loader: 'postcss-loader',
    options: {
      postcssOptions: {
        ident: 'postcss',
        plugins: [require('postcss-preset-env')()]
      }
    }
  }
]

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/index.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // css 处理
      {
        test: /\.css$/,
        use: [...commonCssLoader]
      },

      // less 文件处理 压缩和兼容性处理
      {
        test: /\.less/,
        use: [...commonCssLoader, 'less-loader']
      },

      /* 
        由于js需要被两个loader进行处理,所以需要先执行语法检查之后再来执行兼容性处理。
       */
      {
        // js语法检查 在package.json 中eslintConfig 进行配置
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        // 优先执行
        enforce: 'pre'
      },

      // js 兼容性处理
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                // 进行按需加载
                useBuiltIns: 'usage',
                // corejs 的版本
                corejs: {
                  version: 3
                },
                // 需要兼容的浏览器
                targets: {
                  chrome: '60',
                  ie: '9',
                  firefox: '60'
                }
              }
            ]
          ]
        }
      },
      //图片处理
      {
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[name].[hash:10].[ext]',
          outputPath: 'imgs'
        }
      },
      // html文件中的图片处理
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      // 其他文件处理
      {
        exclude: /\.(js|css|less|html|jpg|png|gif)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[hash:10].[ext]',
          outputPath: 'other'
        }
      }
    ]
  },
  plugins: [
    // 提取css为单独文件
    new MiniCssExtractPlugin({
      filename: 'css/build.css'
    }),

    // css 压缩
    new OptimizeCssAssetsWebpackPlugin(),

    // 处理html文件并压缩
    new HtmlWebPackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
}

webpack性能优化

1. webpack优化配置

  • webpack 性能优化主要是以下几个方面的优化
  • 开发环境性能优化
  • 生产环境性能优化

开发环境优化

  • 优化打包构建速度
  • 优化调试功能

生产环境优化

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

2. HMR 热模块替换

一个模块变化,只更新这一个模块,而不是所有模块,提升加载速度 开启方式:修改 devServer的配置,追加hot: true,即可。

  • 样式文件:可以使用 HMR 功能, style-loader 内部实现了

  • js 文件:默认不可用 HMR 功能, 解决方法:修改 js 文件

    javascript
    if (module.hot) {
      // 如果module.hot 为 true,说明了开启HMR功能,让下面的功能代码生效。
      module.hot.accept("./index.js", function () {
        ...code
        // 意思是指只有这个里面的方法会有HMR的效果,能够实现热模块替换
      });
    }
  • html 文件:默认也不能使用, 缺点:配置了 HMR 后, html 文件也不能热更新了。 解决方法: entry 需要修改,将 html 文件加入入口,并且 html 文件也不需要做热更新

    javascript
    entry: ['./src/js/index.js', './src/index.html'],

2. source-map

一种提供代码构建后映射到源代码的技术,便于构建之后追踪代码错误。

使用时,在 webpack.config.js 中配置 devtool:选项 选项有多种,

  • [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

  • source-map:外部生成一个映射 js,有错误代码准确信息和源代码的错误位置;

  • inline-source-map:内联,将映射表插入到 入口js中,有错误代码准确信息和源代码的错误位置;

  • hidden-source-map:外部,有错误代码错误原因,但是没有错误位置,只能提示构建后代码错误的位置;

  • eval-source-map:内联,每一个文件都生成对应的source-map,都在eval中,构建速度最快,有错误代码准确信息和源代码的错误位置;

  • nosources-source-map:外部,有错误代码准确信息,但是没有任何源代码信息(不能在浏览器中断点调试);

  • cheap-source-map:外部,有错误代码准确信息和源代码的错误位置;

  • cheap-module-source-map:外部,有错误代码准确信息和源代码的错误位置,并且module会将loadersource map加入;

内联 和 外部的区别:

  1. 外部生成了文件,内联没有
  2. 内联构建速度更快

开发环境:速度快,调试更友好 速度快(eval>inline>cheap>...) eval-cheap-souce-mapeval-source-map 调试更友好 souce-mapcheap-module-souce-mapcheap-souce-map

一般使用开发环境使用 eval-source-map / eval-cheap-module-souce-map

生产环境:源代码要不要隐藏? 调试要不要更友好 内联会让代码体积变大,所以在生产环境不用内联

nosources-source-map 全部隐藏 hidden-source-map 只隐藏源代码,会提示构建后代码错误信息 生产环境一般使用: souce-map / cheap-module-souce-map

3. oneOf

一般情况,一个 loader 只会匹配一个类型的文件,除了 js 文件,所以一个文件应该匹配一个 loader 之后就不在匹配其他 loader。 所以在 rules 中 增加一个 oneOf:[] 包裹所有的 rules 。但是 oneOf 中同一个类型文件的 loader 只能有一个,所以需要将处理 js 的一个 loader 移动到外面。

javascript
oneOf: [
  {
    test: /\.css$/,
    use: [...commonCssLoader]
  }
]

4. 缓存

  • 缓存从两方面入手,一方面是 babel 中,开启 babel 的缓存只需要在 babel 中配置一下: cacheDirectory: true,第二次构件时,会读取之前的缓存。

  • 缓存: babel 缓存 直接进行配置就可以 文件资源缓存: hash:每次 webpack 生产文件都会生成唯一的 hash 值, 但是 jscss 同时使用一个 hash 值,如果重新打包,所有的缓存都会失效,需要重新缓存。 chunkhash :根据 chunk 生成的 hash 值,如果打包来源于同一个 chunk ,则 hash 值是同一个。 但是 jscss 是同一个入口打包的,所以 chunkhash 也是相同的。 contenthash :根据文件内容生成 hash 值,不同文件 hash 值一定不一样。 推荐使用contenthash,使用时在 outputcss 文件名中进行配置:

    javascript
    module.exports = {
      output: {
        filename: 'js/index.[contenthash:10].js',
        path: resolve(__dirname, 'build')
      },
    }
    new MiniCssExtractPlugin({
        filename: 'css/build.[contenthash:10].css'
    }),

5. tree shaking

能够去除无用代码 前提:

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

去除无用代码能够减少代码体积,但有时有一些问题,会将没有使用的 css 文件删掉 在package.json中配置 "sideEffects": false 所有代码都没有副作用(都可以进行tree shaking) 增加了下面的配置,能够避免删除一些需要但是没有在index.j调用的文件。 "sideEffects": ["*.css", "*.less"]

5. code split

代码分隔能够提升加载速度,也有一些开发平台限制单文件的大小。

  • 使用单入口或者多入口实现代码分割
javascript
// 单入口
module..exrports={
  entry: "./src/js/index.js";
}
// 多入口
module..exrports={
    entry: {
    main: './src/js/index.js',
    tesst: './src/js/test.js'
  },
}
  • 使用 optimization 配置,可以将 node_modules 中代码单独打包成一个 chunk 输出,能够自动分析多入口 chunk 中,有没有公共的文件,如果有则会打包成一个单独的 chunk
javascript
module..exrports={
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
}
  • js 中使用 import 动态加载,实现代码分割:
javascript
import(/* webpackChunkName:'test' */ '../js/test.js')
  .then((res) => {
    console.log(res.add(1, 3))
  })
  .catch((err) => {
    console.log(err)
  })

6. lazy loading 懒加载

使用动态加载来实现懒加载,只有点击按钮时才会开始加载这个 js 还可以使用 /*webpackPrefetch:true*/ 来实现预加载,预加载会在加载完其他必须的 js 后,浏览器空闲时,自动加载。避免了懒加载时,文件较大,请求时间较长的问题。 预加载的兼容性很差。

javascript
// 懒加载
document.querySelector('#btn').onclick = () => {
  import('./test').then(({ add }) => {
    console.log(add(7, 9))
  })

  // 预加载
  import(/* webpackPrefetch:true */ './test').then(({ add }) => {
    console.log(add(7, 9))
  })
}

7. PMA

渐进式网络开发应用程序,离线也可以访问。由于兼容性有问题,普及率不高。 需要进行安装 npm i workbox-webpack-plugin -D 安装之后需要进行一些配置,主要的功能有

  1. 帮助 serviceworker 快速启动
  2. 删除旧的 serviceworker
  3. 生成一个 serviceworker 配置文件
javascript
new WorkboxWebpackPlugin.GenerateSW({
  clientsClaim: true,
  skipWaiting: true
})

webpack.config.js 中配置完成之后,还需要再入口文件中再配置,并且由于 eslint 不能识别 window、navigator,所以在 package.json 还要配置

javascript
// 注册serviceWorker 注意是大写  处理兼容性问题
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(() => {
        console.log('sw注册成功了~')
      })
      .catch(() => {
        console.log('sw注册失败了~')
      })
  })
}

package.json 的配置:

json
  "eslintConfig": {
    "env": {
      "browser": true
    }
  }

8. 多进程打包

多进程打包,有启动时间和通信时间,所以适合打包工作消耗时间较长的打包来开启。 所以适合 babel-loader 进行打包,使用 thread-loader 开启多进程打包。

javascript
module.exporrts= {
  {
    test: /\.js$/,
    exclude: /node_modules/,
    use: [
      'thread-loader',
      {
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'usage',
                corejs: { version: 3 },
                targets: {
                  chrome: '60',
                  firefox: '50'
                }
              }
            ]
          ],
          cacheDirectory: true
        }
      }
    ]
  },
}

9. externals

externals配置,可以忽略要打包的包名,例如要使用 cdn 加载 jquery 就可以忽略掉 jquery打包,再 index.html 中引入 cdn即可。

javascript
module.exports={
  externals: {
    // 要忽略的打包的包名
    jquery: "jQuery";
  }
}

10. dll

实际上是将需要使用的第三方包 jquery、react、vue 先进行打包,最后打包自己的 js 文件时就可以免去打包第三方包的步骤。 需要新建一个 webpack.dll.js 文件,进行一些基本的配置:

javascript
const { resolve } = require('path')
const webpack = require('webpack')

module.exports = {
  entry: {
    // 最终打包生成的[name] --> jquery
    // ['jquery'] --> 要打包的库是jquery
    jquery: ['jquery']
  },
  output: {
    filename: '[name].js',
    path: resolve(__dirname, 'dll'),
    library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字
  },
  plugins: [
    // 打包生成一个 manifest.json --> 提供和jquery映射
    new webpack.DllPlugin({
      name: '[name]_[hash]', // 映射库的暴露的内容名称
      path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
    })
  ],
  mode: 'production'
}

命令行中使用 webpack --config webpack.dll.js 提前将第三方包进行打包,然后使用 add-asset-html-webpack-plugin 将之前打包出去的包,在自己的项目打包时,引用进来。

注: webpack 包也是必须要引入的。

javascript
const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')

module.exports = {
  plugins: [
    new AddAssetHtmlWebpackPlugin({
      filepath: resolve(__dirname, 'dll/jquery.js')
    })
  ]
}

webpack 配置详细介绍

1. entry 配置

entry 入口的值有三种情况

  1. string: './src/js/index.js' 打包形成一个 chunk ,只会输出一个文件,此时 chunk 的默认名称为 main

  2. array: ['./src/js/index.js', './src/js/add.js'], 所有入口文件最终只会形成一个 chunk,只会输出一个文件。 一般只有 HMR 功能中才会使用

  3. object: { index: './src/js/index.js', add: './src/js/add.js' } 有几个入口文件就会形成几个 chunk,也会输出几个文件。

    特殊用法,所有文件最终只会形成一个 chunk ,输出只有一个文件

    javascript
    {
      index:['./src/js/index.js', './src/js/add.js'],
      add:'./srrc/add.js'
    }

2. output 配置

javascript
module.exports = {
  entry: './src/js/index.js',
  output: {
    // filename 文件名称 + 目录
    filename: './js/[name].js',
    // path 只能指定文件目录
    path: resolve(__dirname, 'build'),
    // 所有资源引入的公共路径前缀 例如:imgs/a.jpg ,打包之后地址变为 /imgs/a.jpg
    publicPath: '/',
    // 非入口 chunk 的名称
    chunkFilename: '[name]_chunk.js',
    // 整个库向外暴露的变量名,一般配合 DLL 进行使用
    library: '[name]',
    // 变量名添加到哪个变量上
    libraryTarget: 'window'
    // libraryTarget:'global'
  },
  plugins: [new HtmlWebPackPlugin()],
  mode: 'development'
}

3. module 配置

javascript
rules: [
  // loader 配置
  {
    test: /\.css$/,
    // 多个用use
    use: ['style-loader', 'cs-loader']
  },
  {
    test: /\.js$/,
    // 排除
    exclude: /node_module/,
    // 只检查 src 下的js文件
    include: resolve(__dirname, 'src'),
    // 单个用loader
    loader: 'eslint-loadere',
    // 优先执行
    enforce: 'pre'
    // 延后执行
    // enforce: 'post'
  },
  {
    // 以下配置只会生效一个
    oneOf: []
  }
]

4. resolve 配置

用于配置解析模块路径别名

javascript
  // 解析模块的规则
  resolve: {
    // 配置解析模块路径别名:能够在js引入import时,简写路径
    alias: {
      $css: resolve(__dirname, 'src/csss')
    },
    // 配置省略文件路径的后缀名
    extensions: ['.js', 'json'],
    // 解析模块时去哪个目录找, 默认是 node_modules
    modules: ['node_modules']
  }

5. DevServer 配置

javascript
  devServer: {
    // 运行代码的目录
    contentBase: resolve(__dirname, 'build'),
    // 监视contentBase目录下的文件,一旦变化就 重载
    watchContentBase: true,
    // 忽略监视的文件
    watchOptions: {
      ignored: /node_modules/
    },
    // 开启压缩
    compreesss: true,
    // 端口号
    port: 5000,
    // 域名
    host: 'localhost',
    // 开启HMR
    hot: true,
    // 不要显示启动服务器日志
    clientLogLevel: 'none',
    // 除了启动信息,其他内容都不显示
    quite: true,
    // 如果出错了,不要全屏显示
    overlay: false,
    // 服务器代理,解决开发环境跨域代理
    proxy: {
      // 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
      '/api': {
        target: 'http://localhost:3000',
        // 发送请求时,请求路径重写:将 /api/xxx 转换为 /xxx (去掉/api)
        pathRewrite: {
          '^/api': ''
        }
      }
    }

  }

6. optimization 配置

javascript
  optimization: {
    splitChunks: {
      chunks: 'all'
      // 默认值,以下都是默认值,跨域不写
      /* minSize: 30 * 1024, // 分割的chunk最小为30kb
      maxSiza: 0, // 最大没有限制
      minChunks: 1, // 要提取的chunk最少被引用1次
      maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
      maxInitialRequests: 3, // 入口js文件最大并行请求数量
      automaticNameDelimiter: '~', // 名称连接符
      name: true, // 可以使用命名规则
      cacheGroups: {
        // 分割chunk的组
        // node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js
        // 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          // 优先级
          priority: -10
        },
        default: {
          // 要提取的chunk最少被引用2次
          minChunks: 2,
          // 优先级
          priority: -20,
          // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
          reuseExistingChunk: true
        }
      }*/
    },

    // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
    // 解决:修改a文件导致b文件的contenthash变化
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}`
    },
    minimizer: [
      // 配置生产环境的压缩方案:js和css
      new TerserWebpackPlugin({
        // 开启缓存
        cache: true,
        // 开启多进程打包
        parallel: true,
        // 启动source-map
        sourceMap: true
      })
    ]
  }