模块支持方案

webpack 支持 CommonJS(配置文件是 Node 环境下运行的),AMD,ES6 Module 规范。

核心概念

从 entry 进入项目,经过 loader、plugin 打包,之后输出 output

entry、output

entry可以是单个字符串,也可以是一个字符串数组(多入口),一般写成对象格式。 key表示名字,value表示入口文件。 详细可以参考官方文档

module.exports = {
  // entry: 'xxx',
  // entry: ['a', 'b'],
  entry: {
    app: "./app.js",
    app2: "./app2.js",
  },
};

output就是输出的结果文件。官方文档

const path = require("path");

module.exports = {
  entry: {
    app: "./app.js",
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[hash:6].[id].[chunkhash].bundle.js", // name对应上面enrty的key
  },
};

loader

webpack的编译方法,webpack自身只能处理JavaScript,所以需要依赖loader来处理别的类型的资源文件。 webpack只能负责打包,相关的编译工作也需要依赖loader处理。 loader本质上只是一个方法,使用时基本需要额外安装。

使用时,在rules数组中使用,loader的执行顺序符合从右往左(从下到上、从数组的最后执行到第一个,compose

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/, // 用正则匹配什么类型的文件
        use: [
          { loader: "style-loader" }, // 用什么loader处理
          {
            loader: "css-loader",
            options: {
              // loader的配置项
              modules: true,
            },
          },
          { loader: "sass-loader" },
        ],
      },
    ],
  },
};

常见的 loader

  • css-loaderstyle-loader等处理cssloader
  • url-loaderimage-loader等图片文字文件等资源处理的loader
  • less-loadersass-loaderbabel-loader等编译loader
  • vue-loader等语法糖loader

plugin

pluginwebpack的额外扩展:

  • 一些插件式的额外功能由plugin定义,帮助webpack优化代码,提供功能。
  • plugin也有一些是webpack自带的,也有需要额外安装的。
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack"); // 访问内置的插件
const path = require("path");

module.exports = {
  entry: "./path/to/my/entry/file.js",
  output: {
    filename: "my-first-webpack.bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: "babel-loader",
      },
    ],
  },
  plugins: [
    new webpack.ProgressPlugin(),
    new HtmlWebpackPlugin({ template: "./src/index.html" }),
  ],
};

常见的 plugin

commonsChunkPluginuglifyjsWebpackPluginPurifyCss等优化文件体积的插件 HtmlWebpackPluginHotModuleReplacementPlugin

编译 ES6

需要安装的 loader

npm install babel-loader @babel/core --save-dev

Babel-preset

presets 是存储 JavaScript 不同标准的插件,通过正确使用 presets,来告诉 babel 按照哪个规范编译。 常见规范:

  • es2015
  • es2016
  • es2017
  • env(通常采用,包括上面的三个和浏览器规范)
  • babel-preset-stage
npm install @babel/preset-env --save-dev

编译 ES6 的方法

babel-polyfill 在打包代码里注入一个全局对象里,对象里定义了 ES6 所有的方法的 ES5 实现。适用于项目开发。 可以在入口文件中直接import 'babel-polifill' 也可以在 entry 中新增 babel-polyfill

babel-plugin-transform-runtime 生成一个局部对象,只会生成使用过的方法的实现。一般适用于框架开发。严格控制大小。

npm install babel-polyfill --save-dev
npm install @babel/plugin-transform-runtime @babel/runtime --save

webpack 配置

module.exports = {
  entry: {
    app: "./app.js",
  },
  output: {
    filename: "[name].[hash:8].js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              [
                "@babel/preset-env",
                {
                  targets: {
                    browsers: [">1%"],
                  },
                },
              ],
            ],
          },
        },
      },
    ],
  },
};

编译 Typescript

  • 安装 Typescript 和 ts-loader
  • 写入 webpack.config.js
  • 配置 tsconfig.json

编译 css

  • css-loader,让 css 可以被 js 正确的引入
  • style-loader,让 css 被引入后可以正确的以一个 style 标签插入页面
  • 必须先过 css-loader,再过 style-loader

style-loader 的一些配置项

  • insertAt : style 标签插入在哪一块区域
  • insertInto : 插入指定的 dom
  • singleton : 是否合并为一个 style 标签
  • transform : 在浏览器环境下,插入 style 到页面前,用 jscss 进行操作

css-loader 的一些核心配置

  • minimize: 是否压缩(webpack4 以上不支持这个,推荐使用 uglifyjsWebpackPlugin)
  • module: 是否进行 css 模块化
  • alias: css 中的全局别名(webpack4 以上不支持这个)

less,sass

css 的预处理语言

less: less,less-loader sass: sass-loader, node-sass

{
  rules: [
      {
        test: /\.less$/,
        use: [{
          loader: "style-loader",
          options: {
            // insertInto: "xxx",
            singleton: true,
            transform: "./transform.js"
          }
        },
        {
          loader: "css-loader",
          options: {
            modules: {
              localIdentName: "[path][name]_[local]_[hash:4]"
            }
          }
        },
        {
          loader: "less-loader"
        }]
      },
    ],
}

提取 css 代码

extract-text-webpack-plugincss 提取为单独的文件

{
  rules: [
      {
        test: /\.less$/,
        use: extractTextCss.extract({
          fallback: {
            loader: "style-loader",
            options: {
              // insertInto: "xxx",
              singleton: true,
              transform: "./transform.js"
            }
          },
          use: [
            {
              loader: "css-loader",
              options: {
                modules: {
                  localIdentName: "[path][name]_[local]_[hash:4]"
                }
              }
            },
            {
              loader: "less-loader"
            }
          ]
        })
      },
    ],

    //  ...
    plugins: [
      new extractTextCss({filename: '[name].min.css'})
    ]
}

css 兼容性处理

post-css,再配合 browserlist

HTML 的处理和打包

html-webpack-plugin

  • filename 指定打包生成的 html 的名字
  • template 指定一个 html 文件为模板
  • minify 压缩 html
  • inject 是否把 jscss 文件插入到 html,插入到哪里
  • chunks 多入口时,指定引入 chunks
new htmlWebpackPlugin({
  filename: "index.html",
  template: "./index.html",
});

环境变量

env 参数可以根据不同的指定,产生不同的配置文件,用来适应生产、测试、开发环境的不同需求

图片处理

url-loaderfile-loaderimg-loader

{
  test: /\.(png|jpg|jpeg|gif)$/,
  use: {
    loader: 'file-loader',
    options: {
      name:'[name].[hash].[ext]',
      outputPath: '',
      publicPath: ''
    }
  }
}

url-loader 可以多一些参数配置

{
  test: /\.(png|jpg|jpeg|gif)$/,
  use: {
    loader: 'url-loader',
    options: {
      name:'[name].[hash].[ext]',
      outputPath: '',
      publicPath: '',
      limit: 5000 //可以将小的资源文件进行base64转码
    }
  }
}

img-loader 可以进行图片压缩

{
  test: /\.(png|jpg|jpeg|gif)$/,
  use: [{
    loader: 'url-loader',
    options: {
      name:'[name].[hash].[ext]',
      outputPath: '',
      publicPath: '',
      limit: 5000 //可以将小的资源文件进行base64转码
    }
  }, {
    loader: 'img-loader',
    options: {
      plugins: [
        require('imagemin-pngquant')({
          speed: 5 // 1-11,越大,压缩率越小
        }),
        require('imagemin-mozjpeg')({
          quality: 80 // 1-100,质量,压缩率
        }),
        require('imagemin-gifsicle')({
          optimizationLevel: 1, // 1-3
        }),
      ]
    }
  }]
}

代码分割

多入口要配置多个 HTMLWebpackPluginfilenamechunks 也要指定好 多页面应用时需要提取公共依赖,打包成一个文件。(主业务代码+公共依赖+第三方包+webpack 运行代码) 单页面应用主要是把需要异步加载改成异步加载,把业务代码和第三方代码拆分,保持纯净。(主业务代码+异步模块+第三方包+webpack 运行代码)

  • webpack3, commonChunksPlugin
  • webpack4, SplitChunksPlugin
{
  optimization: {
    splitChunks: {
      // initial, all ,async
      chunks: "initial",
      minSize: 0,
      // 自定义提取
      cacheGroups: {
        vendor: {
          test: /([\\/]node_modules[\\/])/,
          name: 'vendor',
          chunks: 'all'
        }
      }
    },
    // webpack运行时代码
    runtimeChunk: true,
  }
}

异步模块加载,命名

import(/* webpackChunkName:"mA" */ "./moduleA.js");

require.ensure([], function () {
  require("./moduleA.js");
});

清除之前的 distclean-webpack-plugin

{
  new CleanWebpackPlugin(),
}

体积优化

  • webpack3 optimize.UglifyJsPlugin()
  • webpack4 optimization.minimize

打包加速

项目本身

  • 减少依赖嵌套深度
  • 使用尽可能少的处理

webpack 层面

  • dll 处理
  • 通过 include 减少 loader 范围
  • HappyPack
  • Uglify 优化
  • 减少 resolve,sourcemap,cache-loader,用新版本的 node 和 webpack
// webpack.config.js
{
  new webpack.DllReferencePlugin({
    manifest.require('./src/dll/jquery.json')
  })
}

// webpack.dll.js
const webpack = require("webpack");
module.exports = {
  entry: {
    jquery: ["jquery"],
    loadsh: ["loadsh"],
  },
  output: {
    path: dirname + "/src/dll",
    filename: "./[name].js",
    //引用名
    library: "[name]",
  },
  plugins: [
    new webpack.DllPlugin({
      path: __dirname + "/src/dll/[name].json",
      name: "[name]",
    }),
  ],
};

先运行 webpack --config webpack.dll.js,生成对应文件,之后就可以加速了。

长缓存优化

  • hash -> chunkhash
  • NamedChunksPluginNamedModulesPlugin