Webpack4 快速入坑

435 阅读5分钟
原文链接: github.com

Webpack4 快速入坑

Webpack4官方中文文档

本文小白向,旨在引导入坑Webpack。

配合Demo使用,效果更佳~

一、为什么用Webpack

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)

var modA = require('./a.js')
import modB from './b.js'

最实用的,就是模块打包功能。

Webpack会把这些依赖模块文件内容,通过匹配的loader解析为字符串加入到当前文件代码里,得到的构建产物可以在现代浏览器里正常工作。未来浏览器可能会原生支持导入模块文件,但目前是必须依赖工具来做这种把多个文件打包成一个文件的事情的。

其他工具如grunt、gulp能做到这些吗?其实也能,不过需要插件或者自写逻辑。而Webpack集成了这一功能。

当然,Webpack集成的不止这一个功能,还有更多更多。它们要么在幕后,要么需要配置启用,要么通过loaders、plugins加入进来。

二、怎么用Webpack

环境:nodejs、npm

据我所知,前端工作流工具是运行在nodejs上的。通过nodejs操作文件读写,即可完成构建任务。

基本安装文档已经写得很通俗易懂了,小白照着文档跑几遍Demo即可感受到Webpack的便利。

这里要说的是:不要被文档里大堆的配置吓到,那些配置都是可选项,是入坑后再按实际需要逐个填坑的内容。

在 webpack 4 中,可以无须任何配置使用,然而大多数项目会需要很复杂的设置,这就是为什么 webpack 仍然要支持 配置文件

入坑Webpack,就是有了写 webpack.config.js 的概念,我们脚手架搬砖的同学就需要往配置对象里添砖加瓦。

除了基本的入口文件、输出文件配置,剩下要了解的主要就是loader、plugin了。

loader

loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。

loader是将输入代码转换为输出代码的主要工作者,它可能是完成全部转换任务,也可能是任务中的一个环节。

举个例子:

demo-loader.js

module.exports = function(source){
    source += '\n //用上了demo-loader'
    return source
}

在webpack.config.js中使用:

module.exports = {
    //...
    module:{
        rules: [
            {
                test: /\.js$/,
                use:'demo-loader'   //使用别名
            }
        ]
    },
    resolveLoader:{
        alias:{
            'demo-loader': 'path/to/demo-loader'    //路径别名
        }
    }
}

好了,运行一下webpack,就可以看到构建出的js文件,末尾加了一行注释。

一般情况下,需要对构建产物进行定制,就是通过编写loader进行的,在loader中对输入字符串进行替换,输出字符串加入到后续构建过程中。

如果要对构建过程进行自定义,那么就超出了loader范畴,属于plugin的工作。

plugin

plugins 选项用于以各种方式自定义 webpack 构建过程。

Webpack的构建过程是既定的,不需要自己再写代码了。

要对其进行自定义,就只能通过Webpack预设的一堆钩子,混入其中。

这些钩子的用法,在官方文档里我没找到编写例子,就拿vue-loader-plugin来举例子了:

vue-loader/lib/plugin.js (vue-loader v15新增plugin)

const id = 'vue-loader-plugin'
const NS = 'vue-loader'

class VueLoaderPlugin {
  apply (compiler) {
    // add NS marker so that the loader can detect and report missing plugin
    if (compiler.hooks) {
      // webpack 4
      compiler.hooks.compilation.tap(id, compilation => {
        compilation.hooks.normalModuleLoader.tap(id, loaderContext => {
          loaderContext[NS] = true
        })
      })
    } else {
      // webpack < 4
      compiler.plugin('compilation', compilation => {
        compilation.plugin('normal-module-loader', loaderContext => {
          loaderContext[NS] = true
        })
      })
    }
    //...

感觉Webpack4 API的好处是可以直接查看compiler.hooks得知所有可用钩子,比旧版本查文档更快捷。同时Webpack4 也兼容旧版写法。

官方文档仅列举了可用的钩子,至于怎么使用钩子,全靠compilercompilationloaderContext 三个入参对象承载的大量信息了。在不同钩子中,三个参数的信息会有不同,标志着当前构建状态。只需要在回调函数里执行逻辑,改变入参对象的属性,即可完成自定义构建过程。

三、调试Webpack

说是“调试”Webpack,其实Webpack已经把要开的口子写好了,内部逻辑一般是不需要调试的。

那么我们要调试的就是Webpack开的口子,是怎么输入的?是怎么输出的?运行到哪里了?

也就是调试loader、plugin的代码。

既然Webpack是运行在nodejs上,那么调试的方式就和调试nodejs是一样的。打断点,debug。

我用的WebStorm,找了篇图文并茂的教程。现在问题就剩调试的入口js了。

在命令行运行webpack时,默认是查找webpack.config.js,取导出的配置对象运行的。webpack.config.js作用相当于运行中导入的一个模块,而非入口。

没有入口,就造一个入口。

webpack 提供了 Node.js API,可以在 Node.js 运行时下直接使用。

创建start.js

const webpack = require('webpack')
const webpackConfig = require('path/to/webpack.config.js')
webpack(webpackConfig, (err, stats)=> {
  if (err || stats.hasErrors()) {
    // 在这里处理错误
  }
  // 处理完成
})

start.js配置为debug入口文件,尽情地断点调试吧!

四、小结

自此本文介绍了Webpack4的使用、loader使用与开发调试、plugin开发调试,没有提到webpack4集成的更多复杂功能,实用的如生产环境压缩配置、代码拆分、watch监听等等。官方文档里都有解决方案。

例举了这些坑,目的还是反思:这么多配置,需要了解吗? 答案当然是:不需要!

作为开发者,我们的追求是通过写代码来减少重复劳动、让事情变得简单。Webpack的一系列配置,使得重复劳动简化成了使用配置,活跃的npm开源社区贡献了丰富的loaders、plugins,满足多种多样的应用场景。这些沉淀积累你未必都会用上,你只需要知道怎么去检索这些配置、loaders、plugins,也就是“搬砖”;检索不到的时候,怎么去自写loaders、plugins,也就是“造轮子”。