编写一个Loader

在前面webpack 的配置中,我们使用了许多loader, css-loader, style-loader, vue-loader, file-loader, ...

下面我们来自己写一个Loader.

我们先创建一个文件夹,make-loader ,接着使用 npm init -y 命令对这个文件夹进行node 项目初始化。

然后我们再在项目中安装webpack 和 webpack-cli

npm install webpack webpack-cli --save-dev

然后我们在项目跟目录下创建目录src,在 src 下创建 index.js 文件。

先随意写一个console 语句。

console.log('hello')

然后一般我们的配置打包流程是,在项目跟目录下新建文件webpack.config.js 文件。然后,大概写一下基本的配置项如下。(真实项目的配置项是很多的,这儿将要讲的是loader 故尽量把其他的细节略掉)

const path = require('path')

module.exports = {
    entry: {
        main: './src/index.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}

然后在 package.json 中加入script 命令:

  "scripts": {
    "build": "webpack"
  },

然后,执行npm run build 命令就可以打包index.js 文件了。

好,接下来,我们可以编写一些loader,让打包过程进行一些变更。

比如,我们希望在打包过程中,一旦引用js 文件的时候,遇到 ‘hello’ 字符串 就变成 ‘hello user’ 。这个时候,我们就可以通过Loader 进行修改。

那么,我们开始写一下 Loader 代码。在项目根目录下,创建目录 loaders ,在 loaders 目录下创建文件 replaceLoader.js。

Loader 实际上是一个函数,因此我们可以使用module.export 导出一个函数(不要使用箭头函数,因为在函数内部要使用this,webpack 在调用Loader 时,this 会指向调用函数的那个上下文,那个this包含了一些方法,函数内部可能需要使用。而箭头函数的this 是与调用时无关的,取决于定义的位置)

下面就是这个Loader 的代码实现,其中loader 接收一个参数,是引入文件的源代码或者内容。

module.exports = function (source) {
    return source.replace('hello', 'hello user')
}

那我们在打包的时候,如何使用自己的Loader 呢。

我们打开项目的webpack.config.js 新增module.rules 如下。

const path = require('path')

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    module: {
        rules: [{
            test: /\.js$/,
            use: [path.resolve(__dirname, './loaders/replaceLoader.js')]
        }]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}

然后运行打包命令。就会发现,打包代码中 ‘hello’ 变为了 ‘hello user’ 。

以上就完成了一个最简单的Loader 的例子。

之前,我们在使用某些loader 时,就配置了参数, 比如 url-loader 。那么这儿,我们也可以试试在Loader 中配置参数。下面是webpack.config.js 中,向Loader 中配置一些参数,比如一个名字。

const path = require('path')

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    module: {
        rules: [{
            test: /\.js$/,
            use: [
                {
                    loader: path.resolve(__dirname, './loaders/replaceLoader.js'),
                    options: {
                        name: 'Administrator'
                    }
                }
            ]
        }]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}

那么,在使用这个Loader 打包的时候,就可以把 options 中的配置项传递给 Loader。注意,这个并不是通过函数参数传递的,而是在函数的上下文中的属性中保存,如下在this.query 里。

module.exports = function (source) {
    console.log(this.query)
    return source.replace('hello', 'hello user')
}

好的,下面我们将Loader 代码改一下。

module.exports = function (source) {
    return source.replace('hello', 'hello ' + this.query.name)
}

Nice!

我们翻翻官网 webpack documentation > API > Loader api

https://webpack.docschina.org/api/loaders/#this-query

我们翻到this.query 部分,官网提示,可以使用 loader-utils 中的getOptions 来提前配置中的option。当没配置options 时,使用query 字符串作为参数调用时,需要。

那么,我们可以试一下,先下载loader-utils 

npm install loader-utils --save-dev

然后在loader 代码中,引入,并使用如下。

const loaderUtils = require('loader-utils')

module.exports = function (source) {
    const options = loaderUtils.getOptions(this)
    return source.replace('hello', 'hello ' + options.name)
}

好。还有一个比较常用的 Loader 中的功能,this.callback()

当我们在一个Loader 中将source 进行处理并返回的同时,也对其source map 进行了处理,并希望也返回出去source map的话。一个return 语句就不够了,就会需要this.callback() 。如下。(this.callback 第三个参数应该是source map,这儿没有这个因此就填了source...)

const loaderUtils = require('loader-utils')

module.exports = function (source) {

    const options = loaderUtils.getOptions(this)
    const result = source.replace('hello', 'hello ' + options.name)
    this.callback(null, result, source)
}

有时,在Loader 中要进行一些异步操作。如下。这时我们需要使用this.async

const loaderUtils = require('loader-utils')

module.exports = function (source) {
    const options = loaderUtils.getOptions(this)
    const callback = this.async()
    setTimeout(() => {
        const result = source.replace('hello', 'hello ' + options.name)
        callback(null, result)
    }, 1000)
}

翻阅官网:

下面,我们在项目的loaders 目录下,再创建一个replaceLoaderAsync.js ,让它异步处理js 代码(将hello 改为hello Administrator),如下

const loaderUtils = require('loader-utils')

module.exports = function (source) {
    const options = loaderUtils.getOptions(this)
    const callback = this.async()
    setTimeout(() => {
        const result = source.replace('hello', 'hello ' + options.name)
        callback(null, result)
    }, 1000)
}

然后,再将replaceLoader.js 同步处理js 代码,如下

module.exports = function (source) {
    const result = source.replace('Administrator', 'world')
    this.callback(null, result)
}

我们想先让异步的loader 将字符串改掉,然后同步的loader 又将字符串再改一下。

那我们使用这两个Loader 的时候要注意一下使用顺序。先后顺序:从右到左。因此在webpack.config.js 中我们这样配置。

const path = require('path')

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    module: {
        rules: [{
            test: /\.js$/,
            use: [
                path.resolve(__dirname, './loaders/replaceLoader.js'),
                {
                    loader: path.resolve(__dirname, './loaders/replaceLoaderAsync.js'),
                    options: {
                        name: 'Administrator'
                    }
                }
            ]
        }]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}

上面,寻找自己写的loader 每次都用path 查找,有些麻烦,可以配置webpack的resolveLoader , 让webpack 来依据配置去寻找。如下。(resolveLoader 的配置表示,遇到loader 先去node_modules 中查找,没有就会去./loaders 中查找)

const path = require('path')

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    resolveLoader: {
        modules: ['node_modules', './loaders']
    },
    module: {
        rules: [{
            test: /\.js$/,
            use: [
                'replaceLoader',
                {
                    loader: 'replaceLoaderAsync',
                    options: {
                        name: 'Administrator'
                    }
                }
            ]
        }]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}

Loader 是很有用的,能够在打包的时候对整个项目进行配置修改或者是包装。总之,以后继续实践!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值