在前面的内容中,咱们学习了Webpack的基本知识、经常使用脚手架和性能优化,虽说大部分的开发场景社区已经又成熟的模块给咱们使用,可是遇到特殊状况仍是须要本身有独立开发的能力,所以今天咱们一块儿来学习如何编写自定义Loader。javascript
Webpack中loader是一个CommonJs风格的函数,接收输入的源码,经过同步或异步的方式替换源码后进行输出。java
module.exports = function(source, sourceMap, meta) { }
须要注意的是,该导出函数必须使用function,不能使用箭头函数,由于loader编写过程当中会常常使用到this
访问选项和其余方法。node
咱们先编写一个基本的Loader,完成的工做很简单,那就是把输出的字符串进行替换。webpack
1.新建loader-example目录,执行npm初始化,并安装webpackweb
mkdir loader-example cd loadeer-example npm init -y npm install webpack webpack-cli
2.构建项目目录npm
|----loader # loader目录 |----replace-loader.js # 替换字符串的Loader |----src # 应用源码 |----index.js # 首页 |----package.json |----webpack.config.js
3.编写loader/replace-loader.jsjson
module.exports = function(source) { return source.replace(/World/g, 'Loader'); };
本例中咱们Loader只是简单的将源码中的”World“替换成了”Loader“。性能优化
4.编写src/index.jsbash
console.log('Hello World');
5.编写webpack.config.jsbabel
const path = require('path'); module.exports = { entry: './src/index', target: 'node', // 咱们编译为Node.js环境下的JS,等下直接使用Node.js执行编译完成的文件 output:{ path: path.resolve(__dirname, 'build'), filename: '[name].js' }, module:{ rules:[ { test:/\.js$/, use: 'replace-loader' } ] }, resolveLoader: { modules: ['./node_modules', './loader'] // 配置loader的查找目录 } };
6.编写package.json
{ "scripts":{ "build":"webpack" } }
7.执行构建
npm run build
8.构建完成后,执行build/main.js
node build/main.js
此时终端输出以下,咱们编写的Loader工做正常。
Hello Loader
咱们使用第三方loader时常常能够看到传递选项的状况:
{ test:/\.js$/, use:[ { loader:'babel-loader', options:{ plugins:['@babel/transform-runtime'], presets:['@babel/env'] } } ] }
在Loader编写时,Webpack中官方推荐经过loader-utils来读取配置选项,咱们须要先安装。
npm install loader-utils
咱们给刚才编写的replace-loader传递一个选项,容许自定义替换结果。
const loaderUtils = require('loader-utils'); module.exports = function(source) { const options = loaderUtils.getOptions(this); return source.replace(/World/g, options.text); };
接下来编辑webpack.config.js,给replace-loader传递选项。
module.exports = { module:{ rules:[ { test:/\.js$/, use:[ { loader:'replace-loader', options:{ text: 'Webpack4' } } ] } ] }, resolveLoader:{ modules: ['./node_modules', './loader'] } };
执行构建以后用Node.js执行build/main.js,能够看到输出的内容已经发生变化了。
Hello Webpack4
在Loader中,若是存在异步调用,那么就没法直接经过return返回构建后的结果了,此时须要使用到Webpack提供的回调函数将数据进行回调。
Webpack4给Loader提供了this.async()
函数,调用以后返回一个callback,callback的签名以下:
function callback( err: Error|null, content: string|Buffer, sourceMap?:SourceMap, meta?: any )
例如咱们须要在loader中调用setTimeout进行等待,则相应的代码以下:
module.exports = function(source) { const callback = this.async(); setTimeout(() => { const output = source.replace(/World/g, 'Webpack4'); callback(null, output); }, 1000); }
执行构建,Webpack会等待一秒,而后再输出构建内容,经过Node.js执行构建后的文件,输出以下
Hello Webpack4
默认状况下,资源文件会被转化为 UTF-8 字符串,而后传给 loader。经过设置 raw
,loader 能够接收原始的 Buffer
。好比处理非文本文件时(如图片等等)。
module.exports = function(source) { assert(source instanceof Buffer); return someSyncOperation(source); }; module.exports.raw = true; // 设置当前Loader为raw loader, webpack会将原始的Buffer对象传入
babel-loader在使用时能够加载.babelrc配置文件来配置plugins和presets,减小了webpack.config.js的代码量,便于维护。接下来咱们编写一个i18n-loader,经过读取语言配置文件完成语言转换。
|----loader |----i18n-loader.js # loader |----i18n |----zh.json # 中文语言包 |----src |----index.js # 入口文件 |----webpack.config.js
i18n/zh.json
{ "hello": "你好", "today": "今天" }
loader/i18n-loader.js
const loaderUtils = require('loader-utils'); const path = require('path'); module.exports = function (source) { const options = loaderUtils.getOptions(this); const locale = options ? options.locale : null; // 读取语言配置文件 let json = null; if (locale) { const filename = path.resolve(__dirname, '..', 'i18n', `${locale}.json`); json = require(filename); } // 读取语言标记 {{}} const matches = source.match(/\{\{\w+\}\}/g); for (const match of matches) { const name = match.match(/\{\{(\w+)\}\}/)[1].toLowerCase(); if (json !== null && json[name] !== undefined) { source = source.replace(match, json[name]); } else { source = source.replace(match, name); } } return source; }
src/index.js
console.log('{{Hello}}, {{Today}} is a good day.');
webpack.config.js
const path = require('path'); module.exports = { entry: './src/index', output: { path: path.resolve(__dirname, 'build'), filename: '[name].js' }, target: 'node', module: { rules: [ { test: /\.js$/, use: [ { loader: 'i18n-loader', options: { // 传递选项 locale: 'zh' } } ] } ] }, resolveLoader: { modules: ['./node_modules', './loader'] } };
package.json
{ "scripts":{ "build":"webpack" } }
npm run build
构建完毕后使用Node.js执行build/main.js输出以下:
你好, 今天 is a good day.
能够看到i18n-loader成功读取了配置文件。
本文简要介绍了Webpack中如何编写一个自定义的loader,权当抛砖引玉,更多的用法等待读者在实际工做中去挖掘,要想掌握Webpack的高级知识,Loader是必不可少的技能,有时候若是社区找不到合适的Loader,你们能够根据须要本身进行开发。