前言

这是我花了几个星期学习webpack4的学习笔记。内容不够细,因为一些相对比较简单的,就随意带过了。希望文章能给大家带来帮助。如有错误,希望及时指出。例子都在learn-webpack仓库上。如果你从中有所收获的话,希望你能给我的github点个star

编写loader

// index.jsconsole.log('hello, atie')

配置webpack.config.js

// webpack.config.jsmodule: {  rules: [    {      test: /\.js$/,      include: /src/,      loader: path.resolve(__dirname, './loaders/replaceLoader.js')    }  ]},
// 函数不能使用箭头函数module.exports = function(source) {    console.log(source, 'source')    return source.replace('atie', 'world')}

loader文件其实就是导出一个函数,source就是webpack打包出的js字符串。这里的loader就是将上面的console.log('hello, atie')替换为console.log('hello, world')

打包下代码,不出所料。控制台就会打印出hello, world

当你想要给loader传参时,可配置如下

module: {  rules: [    {      test: /\.js$/,      include: /src/,      use: [{        loader: path.resolve(__dirname, './loaders/replaceLoader.js'),        options: {          name: 'haha'        }      }]    }  ]},

通过给loader添加options

这样loader中就可以通过this.query获取该参数了

module.exports = function(source) {      // 控制台输出:console.log('hello atie') { name: 'haha' } source    console.log(source, this.query, 'source')    return source.replace('atie', 'world')}

当然变量不一定非要通过this.query来获取

可通过loader-utils这个包来获取传入的变量

安装: npm i loader-utils -D

const loaderUtils = require('loader-utils')// 函数不能使用箭头函数module.exports = function(source) {    // console.log(source, this.query, 'source')    const options = loaderUtils.getOptions(this)    console.log(options, 'options') // { name: 'haha' } 'options'    return source.replace('atie', 'world')}

打印出来的与上面this.query一致

上面都是直接通过return返回的,那么我们还有没有其他方法返回loader翻译后的代码呢?`

这里就会用到callback

this.callback(  err: Error | null,  content: string | Buffer,  sourceMap?: SourceMap,  meta?: any);

上面的代码就可以改写成

module.exports = function(source) {    const options = loaderUtils.getOptions(this)    const result = source.replace('atie', options.name)    this.callback(null, result)}

callback优势在于它可以传递多余的参数

module.exports = function(source) {    setTimeout(() => {        return source.replace('atie', 'world')    }, 1000)}

当我们把return包到异步方法里,打包的时候就会报错,那么我们该怎么办呢?

这个时候就需要用到this.async()

module.exports = function(source) {    const callback = this.async()    setTimeout(() => {        callback(null, source.replace('atie', 'world'))    }, 2000)}

通过调用this.async()返回的callback方法来返回结果

use中的loader执行顺序,先右后左,先下后上

编写plugin

在根目录下新建plugins文件夹,并新建copyright-webpack-plugin.js,内容如下:

class Copyright {    constructor() {        console.log('this is plugin')    }    apply(compiler) {    }}module.exports = Copyright

注意:apply这个方法必须存在,不然插件被执行的时候会报错。

配置webpack.config.js,如下:

const Copyrgiht = require('./plugins/copyright-webpack-plugin.js')...plugins: [  new Copyrgiht()]

执行下打包命令后

this is pluginHash: 479baeba2207182096f8Version: webpack 4.30.0Time: 615msBuilt at: 2019-05-08 23:05:08     Asset       Size  Chunks             Chunk Names bundle.js   3.77 KiB    main  [emitted]  mainindex.html  182 bytes          [emitted]  

控制台打印出了this is plugin

接下来,我们继续探索插件的奥秘

在使用插件的时候还可以传参

// webpack.config.jsplugins: [  new Copyrgiht({    name: 'atie'  })]
class Copyright {    constructor(options) {        // console.log(options, 'this is plugin')          this.options = options    }    apply(compiler) {          console.log(this.options)    }}

执行下打包命令:

{ name: 'atie' }Hash: 479baeba2207182096f8Version: webpack 4.30.0Time: 742msBuilt at: 2019-05-08 23:24:10     Asset       Size  Chunks             Chunk Names bundle.js   3.77 KiB    main  [emitted]  mainindex.html  182 bytes          [emitted]

控制就会输出 {name: 'atie'}

webpack在调用apply会传递一个compiler参数,这个参数可以做很多事情,具体可以参考webpack官网

这里介绍下钩子

class Copyright {    apply(compiler) {        compiler.hooks.emit.tapAsync('Copyright', (compilation,callback) => {            console.log(compilation.assets, '以具有延迟的异步方式触及 run 钩子。');            compilation.assets['copyright.txt'] = {                source: function() {                    return 'copyright by atie'                },                size: function() {                    return 17                }            }            callback()        })    }}module.exports = Copyright

该钩子是在文件生成前触发的。我们在文件生成前,在asset对象上在加一个文件对象

打包之后

.├── bundle.js├── copyright.txt└── index.html

可以看到多了一个copyright.txt,也就是我们上面创建的文件。点开该文件还会看到里面的内容正是copyright by atie