webpack学习记录(0)-核心功能简述

613 阅读5分钟

前言-为什么要使用webpack

  • 转换ES6
  • 转换jsx(vue、ts等 )
  • css前缀补全/预处理器(sass、less等)
  • 图片压缩
  • 压缩混淆

配置⽂件名称

webpack 默认配置⽂文件:webpack.config.js,可通过webpack --config来指定配置文件。

webpack.config.js中,除了我们常用的导出对象的方式,还有多种配置类型

  • 导出为一个函数
module.exports = function(env, argv) {
  return {
    mode: env.production ? 'production' : 'development',
    devtool: env.production ? 'source-maps' : 'eval',
    ...
  };
};

该函数会传入两个参数。一个是环境对象(environment),另一个是map对象。

  • 导出一个 Promise

这种方法便于需要异步地加载所需的配置变量。

module.exports = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        entry: './app.js',
        /* ... */
      })
    }, 5000)
  })
}
  • 导出多个配置对象

当运行webpack时,所有的配置对象都会构建。例如,导出多个配置对象,对于针对多个构建目标(例如 AMD 和 CommonJS)打包一个 library 非常有用。

module.exports = [{
  output: {
    filename: './dist-amd.js',
    libraryTarget: 'amd'
  },
  entry: './app.js',
  mode: 'production',
}, {
  output: {
    filename: './dist-commonjs.js',
    libraryTarget: 'commonjs'
  },
  entry: './app.js',
  mode: 'production',
}]

下面我们介绍一些配置文件中的核心配置概念。

核心概念--Entry

入口起点(entry point)指示webpack应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack会找出有哪些模块和库是入口起点(直接和间接)依赖的。

我们可以在entry设置一个或多个入口文件,默认值为 ./src

  • 设置单个入口

用法:entry: string|Array

// webpack.config.js
module.exports = {
  entry: './path/to/my/entry/file.js'
};

向 entry 属性传入「文件路径(file path)数组」将创建“多个主入口(multi-main entry)”。在你想要多个依赖文件一起注入,并且将它们的依赖导向(graph)到一个“chunk”时,传入数组的方式就很有用。

即当使用Array<string>时,仍会仅生产一个bundle文件(下同,会生成对应的一个chunk文件)。

  • 设置多个入口

用法:entry: {[entryChunkName: string]: string|Array}

// webpack.config.js
module.exports = {
  app: './src/app.js',
  vendors: './src/vendors.js'
};

上面的单个入口同下面的配置:

// webpack.config.js
module.exports = {
  main: './path/to/my/entry/file.js'
};

常见场景

  • 多页面应用程序
const config = {
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
  }
};

每个HTML页面单独引入一个index.js,需要3个分离的依赖图。

这里可以使用CommonsChunkPlugin来抽取公共逻辑代码,减少。

当然,这种写法在每次新增或删除⻚页⾯,都需要改webpack配置。这里我们可以使用glob.sync来动态设置。

entry: glob.sync(path.join(__dirname, './src/*/index.js'))
  • 用于实现组件库的压缩版本和非压缩版本
const config = {
  entry: {
    "index": "./src/index.js",
    "index.min": "./src/index.js"
  },
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        include: /\.min\.js$/,
      }),
    ],
  }
};

这里我们使用TerserPlugin来实现代码压缩(后面介绍),并且仅压缩*.min.js文件。

当然,在使用的该组件库的时候,我们可以设置package.jsonmainindex.js,在该文件中动态引入压缩版本和非压缩版本。

// index.js
if (process.env.NODE_ENV === "production") {
  module.exports = require("./dist/index.min.js");
} else {
  module.exports = require("./dist/index.js");
}

核心概念--Output

配置output选项可以控制webpack如何向硬盘写入编译文件。注意,即使可以存在多个入口起点,但只指定一个输出配置。

output属性的最低要求为必须是一个对象,且具有以下两个属性(更多的属性说明见这里):

  1. filename,用于输出文件的名称
  2. path,用于输出文件所在目录的绝对路径
  • 单入口配置
module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
      filename: 'bundle.js’,
      path: __dirname + '/dist'
  },
};
  • 多入口配置
module.exports = {
  entry: {
      app: './src/app.js',
      vendors: './src/vendors.js'
  },
  output: {
      filename: '[name].js',
      path: __dirname + '/dist'
  },
};

由于多个入口,会生成多个bundle文件,所以这里使用占位符来进行区分。

核心概念-Loaders

webpack开箱即用只支持JS和JSON两种文件类型,需要loader将其他类型的文件转为webpack能够处理的文件类型,并添加到依赖图中完成打包。

  • loader支持链式传递。

loader类似于其他构建工具中“任务(task)”,本身是一个函数。传递链中的第一个loader将返回值传递给下一个loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。

  • loader可以是同步的,也可以是异步的。
  • loader接收查询参数,用于对loader传递配置;也能够使用options对象进行配置。

常见的loader

名称描述
babel-loader转换ES6等JS新语法特性
ts-loader将TS转为JS
css-loader支持.css文件的加载和解析
less-loader将less文件转为.css文件
file-loader处理图片文件
raw-loader将文件以字符串的形式导入,比如.txt文件
less-loader将less文件转为.css文件
thread-loader多进程打包css和js文件

更多loader可以参考这里

使用loader

我们一般在配置文件中通过module.rules来设置loader使用方式。

注:还有内联方式(每个import语句中显式指定loader)和CLI方式,但是不推荐。

module.exports = {
  module: {
    rules: [
      { test: /\.css$/, use: 'css-loader' },
      { test: /\.ts$/, use: 'ts-loader' }
    ]
  }
};

其中,test用于匹配待处理的文件;use表示使用的loader,当需要使用多个loader时,use的值可以为数组,其loader执行顺序为从右到左

module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
    ]
  }

如上所示,处理.css文件时,会先执行css-loader,再执行style-loader

此外,除了调整loader的顺序,也可以设置loaderenforce属性来调整执行的优先级。

  • pre 优先处理
  • normal 正常处理(默认)
  • inline 其次处理(不推荐)
  • post 最后处理
module: {
    rules: [
      {
        test: /\.less$/,
        use: 'less-loader',
        enforce: 'pre'
      },
     {
        test: /\.less$/,
        use: 'css-loader'
      },
     {
        test: /\.less$/,
        use: 'style-loader',
        enforce: 'post'
      }
    ]
  },

在上面的配置文件,loader的执行顺序就是:less-loader-->css-loader-->style-loader

核心概念-Plugins

插件是webpack的支柱功能,用于bundle文件的优化,资源管理理和环境变量量注⼊入,即解决loader无法处理的事情,会作用于整个构建过程。

插件是一个具有apply方法的JavaScript对象。apply方法会被webpack compiler调用,并且在整个编译生命周期都可以访问compiler对象。

插件就是一个含有apply方法的类,在apply方法中会传入compiler(即webpack实例)。我们可以通过调用compiler中暴露的钩子函数,完成在webpack构造过程的相关功能。

class MyExampleWebpackPlugin {
  apply(compiler) {
    compiler.hooks.emit.tapAsync(
      'MyExampleWebpackPlugin',
      (compilation, callback) => {
        ...
        callback();
      }
    );
  }
}

这里调用的钩子函数的tapAsync方法(异步调用),取决于不同的钩子类型,也可以在某些钩子上访问tap(同步调用)和tapPromise

更多插件相关内容可参考这里

常用插件

名称说明
CommonsChunkPlugin提取块之间共享的通用模块
CopyWebpackPlugin将单个文件或整个目录复制到构建目录
CleanWebpackPlugin清理构建目录
MiniCssExtractPlugin将CSS单独提取为一个文件
HotModuleReplacementPlugin启用热模块更换(HMR)
HtmlWebpackPlugin轻松创建HTML文件来服务您的捆绑软件
TerserPlugin使用Terser缩小项目中的JS

更多插件内容可以参考这里

Webpack构建流程

Webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

  1. 初始化参数

从配置文件和CLI配置语句中读取与合并参数,得出最终的参数。

  1. 开始编译

用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译。

  1. 确定入口

根据配置中的entry找出所有的入口文件

  1. 编译模块

从入口文件出发,调用所有配置的loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。

  1. 完成模块编译
  2. 输出资源

根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk文件,再把每个chunk文件转换成一个单独的bundle文件加入到输出列表。

  1. 输出完成

在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

总结

Webpack配置文件中,通过entry设置编译入口文件,在output设置编译后的文件目录,通过loader引入其他类型文件,通过plugin来实现一些loader无法达到的功能。

参考