原文地址
编写 loader 和 plugins
github
一、loader
1.loader 介绍
loader 是什么
loader 其实是一个函数,对匹配到的内容进行转换,将转换后的后果返回。
loader 作用
在
webpack
中loader
就像是一位翻译官。webpack
只意识JavaScript
这们语言,对于其余的资源通过loader
后能够转化做预处理
- loader 的执行是有程序的,
反对链式的调用
。loader 的执行程序是从下到上,从右到左。比方解决款式类的文件,use:['style-loader', 'css-loader']
。css-loader
解决后的文件返回给style-loader
。 - 一个 Loader 的职责是繁多的,只须要实现一种转换。
- Webpack 会默认缓存所有 Loader 的处理结果,对没有批改的 loader 不会从新加载,敞开 webpack 的默认缓存后果须要增加
this.cacheable(false);
常见的 loader
- 款式类的 loader:
css-loader, style-loader, less-loader, postcss-loader(增加 -webkit)
等 - 文件类的 loader:
url-loader, file-loader, raw-loader
等。 - 编译类的 loader:
babel-loader, ts-loader
等 - 校验测试类 loader:
eslint-loader, jslint-loader
等
4. loader 的三种应用形式
-
-
在
webpack.config.js
中配置module.exports = { module:{ rules:[{ test:/\.css$/, use:['css-loader'], // use:{loader:'css-loader',options:{}} } ] } }
-
-
-
通过命令行的参数形式
webpack --module-bind 'css=css-loader'
-
-
-
通过内联应用
import txt from 'css-loader!./file.css';
-
2. 编写一个 loader
思路:后面咱们说过
1.loader 是一个函数;2. 对匹配到的内容进行转换;3. 再将转换内容返回
,依照这个思路咱们能够编写一个最简略的loader
// 在 ./loader/replaceLoader.js 创立一个替换字符串的 loader
module.exports = function(source) {return source.replace('a', 'b')
}
// 在 webpack.config.js 应用 本人写的 loader
module.exports = {
module:{
rules:[{
test:"/\.js$/",
use:[{loader: path.resolve(__dirname, './loader/replaceLoader.js')
options:{name: '林一一'}
}
]
}]
}
}
// 或者应用 replaceLoader
module.exports={resolveLoader:['node_modules', './loader']
module:{
rules:[{
test:"/\.js$/",
use:['resolveLoader']
}]
}
}
下面就是一个最简略的编写 loader 的案例
-
loader 还能够接管
options
传入的参数,详情查看 loader API,也能够应用官网提供的loader-util
接管参数const loaderUtil = require('loader-utils') module.exports = function(source) {console.log(this.query.name) // 林一一 const options = loaderUtil.getOptions(this) return source.replace('a', 'b') }
-
异步:loader 是一个函数天然有同步和异步的辨别。应用异步的 loader 须要增加
this.async()
申明异步操作const loaderUtils = require('loader-utils') module.exports = function(source) {const options = loaderUtils.getOptions(this) const callback = this.async() setTimeout(()=>{console.log(options.name) let res = source.replace('a', options.name) callback(null, res, sourceMaps, ast) }, 4000) }
下面的代码会在 4 秒后打包胜利,如果没有
this.async()
异步操作就会失败,callback()
回调函数将后果放回。 - 默认状况下 webpack 给 loader 传递的字符串编码是
utf-8
,如果须要解决二进制的文件须要增加exports.raw = true
。 - 下面提到过 webpack 会默认将
loader
的加载后果缓存如果须要敞开webpack
的缓存后果须要增加this.cacheable(false);
。 Npm link
专门用于开发和调试本地 Npm 模块,在没有公布到 npm 下面也能够在调式本地的loader
。具体须要在package.json
中配置本地 loader
,在根目录下执行npm link loader-name
就能够在node_modules
中应用本地的loader
了。同时也能够采纳下面的resolveLoader
实现导入loader
的形式
总结编写 loader 的思路
- loader 是一个导出函数,有返回值,能够借助第三方模块和 Node api 实现。
- loader 能够应用
loader-utils
接管到options
中传递过去的参数- loader 的异步编写须要显示的申明
const callback = this.async()
表明异步。- loader 如果须要解决二进制文件也须要申明
exports.raw = true
- loader 的容许后果会被 webpack 缓存,如果须要敞开
webpack
的缓存后果须要申明this.cacheable(false)
- 编写后的本地
loader
能够借助Npm link
或resolveLoader
导入。
二、webpack 的构建流程
再讲 plugins 之前须要先分明 webpack 的构建流程是怎么的
初始化参数
。从配置文件和shell
语句中合并的参数开始编译
。将上一步失去的参数初始化成complier 对象
,加载所有的导入插件,执行对象的 run 办法开始执行编译;确定入口
。从配置的entry
入口找出所有的入口文件。编译模块
。依据入口文件的依赖,调用所有配置的loader
进行转换。实现模块编译并输入
。依据入口文件之间的依赖关系,造成一个个代码块chunk
。输入实现
。将造成的代码块chunk
输入到文件系统。
下面初始化造成的
complier
对象 会被注入到插件的apply(complier)
内。complier
对象对象蕴含了 Webpack 环境所有的的配置信息比方options, loaders, plugins
等等属性,能够简略的认为complier
是 webpack 的实例,通过compler.plugin()
能够监听到webpack
播送进去的事件。
三、plugin
1 plugin 介绍
plugin 是什么
plugin 是一个插件,这个插件也就是一个类,基于事件流框架
Tapable
实现。在 webpack 的构建流程中在初始化参数后,就会加载所有的plugin
插件,创立插件的实例。
plugin 作用
plugin
通过钩子能够波及到webpack
的整一个事件流程。也就是说plugin
能够通过监听这些生命周期的钩子在适合的机会应用webpack
提供的 API 做一些事件。
常见的 plugin
html-webpack-plugin
会在打包后主动生成一个html
文件,并且会将打包后的 js 文件引入到html
文件内。optimize-css-assets-webpack-plugin
对 CSS 代码进行压缩。mini-css-extract-plugin
。将写入style
标签内的 css 抽离成一个 用link
导入 生成的 CSS 文件webpack-parallel-uglify-plugin
。开启多过程执行代码压缩,进步打包的速度。clean-webpack-plugin
。每次打包前都将旧生成的文件删除。serviceworker-webpack-plugin
。为网页利用减少离线缓存性能。
plugin 的应用形式
在
plugins
中应用
const ServiceworkerWebpackPlugin = require('serviceworker-webpack-plugin')
module.exports = {
plugins:[new ServiceworkerWebpackPlugin(),
]
}
2 编写一个 plugin
思路:
plugins
是一个类,webpack
为 plugin 提供了很多内置的 api,须要在原型上定义apply(compliers)
函数。同时指定要挂载的webpack
钩子。
class MyPlugin {constructor(params){console.log(params)
}
// webpack 初始化参数后会调用这个援用函数,闯入初始化的 complier 对象。apply(complier){
// 绑定钩子事件
// complier.hooks.emit.tapAsync()
compiler.plugin('emit', compilation => {console.log('MyPlugin')
))
}
}
module.export = MyPlugin
compilation 对象
蕴含以后的模块资源、编译生成资源、和能监听变动的文件。每一个文件发生变化后,都会生成一个compilation
对象,通过compilation
也能读取到compiler
对象。
调式plugin
能够应用 node 的调式工具在package.json
中增加"debug":"node --inspect --inspect brk node_modules/webpack/bin/webpack.js"
总结编写 plugin 的思路
- 编写一个 class 类。
- 在类中定义一个 apply 办法。
- 在利用办法
apply()
中指定挂载的webpack
事件钩子complier.hooks.
。- 解决 webpack 外部实例的特定数据。
- 性能实现后调用 webpack 提供的回调。
四、面试题
1. loader 和 plugin 的区别
- loader 是一个函数,用来匹配解决某一个特定的模块,将接管到的内容进行转换后返回。在
webpack
中操作文件,充当文件转换器的角色。在modules.rules
中配置。- plugin 是一个插件,不间接操作文件,基于事件流框架
Tapable
实现,plugin
通过钩子能够波及到webpack
的整一个事件流程。也就是说plugin
能够通过监听这些生命周期的钩子在适合的机会应用webpack
提供的 API 做一些事件。在plugins
中配置插件
2. loader 的编写思路
参上
3. plugin 的编写思路
参上
4. complier 和 compilation 区别
- complier 对象裸露了 webpack 整一个生命周期相干的钩子,是
webpack
初始化的参数的产物,蕴含options, entry, plugins
等属性能够简略的了解为webpack
的一个实例。compilation
对象是complier
的实例,是每一次webpack
构建过程中的生命周期对象。每一个文件发生变化后都能生成一个complition
对象。
总结:两个对象都有本人的生命周期钩子,compilation 对象
负责的是粒度更小的生命周期钩子。compiler 对象
是 webpack 整一个整个生命周期钩子的对象。
参考
webpack 之 loader 和 plugin 简介
webpack 构建流程
webpack loader 和 plugin 编写
深刻 Webpack- 编写 Loader