Loader 和 Plugins 的区别
loader 主要的是处理静态资源,而 plugins 是可以贯穿在整个 webpack 构建的周期中,他能做到 loader 做不到的事情。但是,loader 他可以用独立的运行环境,可以在本地使用一些库进行本地发发调制,而 plugins 不行,他必须编写好这个 plugin 之后在 webpack 构建中将 plugin 放在 plugins 的数组中执行。
loader 开发的常用的开发方法
webpack 文档 DEMO 结构分析
import { getOptions } from 'loader-utils';
import { validate } from 'schema-utils';
const schema = {
type: 'object',
properties: {
test: {
type: 'string',
},
},
};
export default function (source) {
const options = getOptions(this);
validate(schema, options, {
name: 'Example Loader',
baseDataPath: 'options',
});
// Apply some transformations to the source...
return `export default ${JSON.stringify(source)}`;
}
loader 实际上就是一个 js 模块,提供一个方法对原文件进行逻辑操作,处理完毕之后返回回去的一个过程。顺带提一点就是,loader 的链式调用是从后往前。
同步 loader 参数获取
参数获取可以使用一个 叫 loader-utils 的 loader,使用其中的 getOptions 的方法就可以拿到传递的参数。 在 runLoaders 配置中 loaders 参照文档修改为带 options 的配置,举例加上一个对象:
runLoaders({
loaders: [
// path.join(__dirname, './loaders/raw-loader.js'),
{
loader: path.join(__dirname, './loaders/raw-loader.js'),
options:{
env: 'development',
version: '1.0.0'
},
}
],
...})
之后使用 loader-utils 将传递的参数带出。注意,这里的 module.exports 不能使用箭头函数,否则,this 是指向了当前的作用域,就拿不到 runLoaders 里面的属性了。
// npm install loader-utils -save-dev
const loaderUtils = require('loader-utils');
module.exports = function (source) {
console.log(this);
const { env, version } = loaderUtils.getOptions(this);
console.log(env);
console.log(version);
...
};
// npmjs -> https://www.npmjs.com/package/loader-utils
同步 loader 异常处理
同步执行的情况下,处理异常错误打印的方式有两种:
第一种:是直接使用 Error 输出错误,可以在信息内填写错误编号
module.exports = function (source) {
...
return new Error('error:10001');
};
第二种:是直接使用 this.callback ,根据参数的不同,再执行下一步或者是输出报错信息。
module.exports = function (source) {
...
return this.callback(new Error('error:10001'), source);
};
this.callback 的第一个参数是 Error 时,表示异常直接报错,如果第一个参数是 null ,那就说明函数正常执行下一步返回数据, 同时也支持多个参数进行传递,代码如下:
module.exports = function (source) {
...
const params = { key: 1 }
return this.callback(null, source, params);
};
异步 loader 处理
使用异步获取结果需要使用 this.async() 这个方法,作为一个 callback 使用,第一个参数是判断是否错误,第二个直接传递参数。
module.exports = function (source) {
...
const callback = this.async();
fs.readFile(path.join(__dirname, '../src/number.txt'), 'utf-8', (err, data) => {
if(err){
callback(err,'error')
}
callback(null, data);
});
};
loader 输出文件
使用 emitFile 进行输出。
const loaderUtils = require('loader-utils');
module.exports = function (source) {
const url = loaderUtils.interpolateName(this, '[name].[ext]', source);
this.emitFile(url, source)
return source;
}
开发适用于活动的 CSS Sprites loader
首先,需要使用 spritesmith 这个依赖,将多张图片和合并到一起。
第一步:读取图片 URL
思路是先将 css 文件获取到,再使用正则匹配导出所有的图片地址
const loaderUtils = require('loader-utils');
module.exports = function (source) {
const images = source.match(/url\((\S*)/g);
const matchedImages = [];
if (images && images.length > 0) {
for (let i = 0; i < images.length; i++) {
const img = images[i].match(/url\(..(\S*)\'/)[1];
matchedImages.push(path.join(__dirname, img));
}
}
}
再把文件的目录提取出来,转换成一个绝对地址 push 到一个数组里面,打印出来的 matchedImages 如下:
[
'/Users/../images/web-security-bg-1.jpeg',
'/Users/../images/webpack-images-2.png'
]
第二步:使用 spritesmith 合并图片和 css 地址替换
Spritesmith.run({ src: matchedImages}, (err, result) => {
fs.writeFileSync(path.join(process.cwd(), 'dist/sprite.png'), result.image);
source = source.replace(/url\((\S*)/g, (match) =>{
return `url("dist/sprite.jpg")`;
});
fs.writeFileSync(path.join(process.cwd(), 'dist/index.css'), source);
callback(null,source); })
在 dist 的目录中,就会出现一个合并好的图片同时 dist 里面还有一个已替换了 sprite 图的 css 文件 ,当然这里只是说明了一个思路,如果要完全的实现图片和样式的替换还需要考虑到背景大小,定位或者是一些边界问题,这里就不再细说了。
webpack 的 Plugin 的常用的开发方法
webpack 文档 DEMO 结构分析
// webpack writting a plugin -> https://webpack.js.org/contribute/writing-a-plugin/
// A JavaScript class.
class HelloWorldPlugin {
apply(compiler) {
compiler.hooks.done.tap('Hello World Plugin', (
stats /* stats is passed as an argument when done hook is tapped. */
) => {
console.log('Hello World!');
});
}
}
module.exports = HelloWorldPlugin;
plugin 实际上一个一个类,在 webpack 使用 plugin 的时候都会有 new XXplugin() 的操作,就是新建一个 plugin 的类。apply 是 plugin 在 webpack 是每一次构建的时候都会运行。
hooks 是 compiler 对象的一个钩子,也可以说是可以监听在某个阶段做一些什么样的事情。会掉的最后就是这个 plugin 的逻辑代码。
开发一个压缩构建资源为 zip 包的 plugin
创建 zip 文件
首先,还是先使用一个 jszip 它可以将文件压缩成一个 zip 包,使用 compiler 对象的 hooks 的 emit 钩子,生成一个文件。
const JSZip = require('jszip');
const zip = new JSZip();
compiler.hooks.emit.tapAsync('ZipPlugin', (Compilation, callback) => {
const folder = zip.folder(this.options.filename);
for (let filename in Compilation.assets) {
const source = Compilation.assets[filename].source();
folder.file(filename, source)
}
})
使用 zip.folder 先设置 zip 包的名称, 在触发到了emit 的 tapAsunc 异步钩子的时候,处理 compilation 的内容填充到 folder 里面去。 compilation.assist 是一个目录名,遍历出文件名之后使用 source 拿到 source 在放回 folder 中。
第二步将文件输出为 zip
zip.generateAsync({
type:'nodebuffer'
}).then((content)=>{
const outputPath = path.join(Compilation.options.output.path ,this.options.filename + '.zip');
const outputRelativePath = path.relative(
Compilation.options.output.path,
outputPath
)
Compilation.assets[outputRelativePath] = new RawSource(content);
callback()
console.log(Compilation.options)
})
})
使用 zip.generateAsync 返回的 content 是一个 buffer ,需要使用 RawSource 将这个 buffer 转换挂到 compilation 的 assist 中 ,最后执行 callback 。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!