webpack的loader和plugin的区别

4,171 阅读6分钟

loader

loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中,处理一个文件可以使用多个loaderloader的执行顺序和配置中的顺序是相反的,即最后一个loader最先执行,第一个loader最后执行,第一个执行的loader接收源文件内容作为参数,其它loader接收前一个执行的loader的返回值作为参数,最后执行的loader会返回此模块的JavaScript源码。

编写自己的loader时需要引用官方提供的loader-utils,调用loaderUtils.getOptions(this)拿到webpack的配置参数,然后进行自己的处理。

Loader本身仅仅只是一个函数,接收模块代码的内容,然后返回代码内容转化后的结果,并且一个文件还可以链式的经过多个loader转化(如 scss-loader => css-loader => style-loader)。

一个Loader的职责是单一的,只需要完成一种转化。如果一个源文件需要经历多步转化才能正常使用,就通过多个Loader去转化。在调用多个Loader去转化一个文件时,每个Loader会链式的顺序执行,第一个Loader将会拿到需要处理的原内容,上一个Loader处理后的结果会传给下一个接着处理,最后的Loader将处理后的最终结果返回给Webpack

一个最简单的Loader例子:

module.exports = function(source) {
  // source为compiler传递给loader的一个文件的原内容
  // 该函数需要返回处理后的内容,这里为了简单示例,直接把原内容返回了,相当于该Loader没有做任何转换
  return source
}

plugin

plugin功能更强大,loader不能做的都是它做。它的功能要更加丰富。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。

pluginwebpack的机制更加灵活,它在编译过程中留下的一系列生命周期的钩子,通过调用这些钩子来实现在不同编译结果时对源模块进行处理。它的编译是基于事件流来编译的,主要通过tapable来实现插件的绑定和执行的,tapable主要是基于发布订阅的插件架构,是用来创建生命周期钩子的库。调用compiler.hooks.run.tap开始注册,创建compilation,基于配置创建chunks,在通过parser解析chunks,使用模块和依赖管理模块之间的依赖关系,最后使用template基于compilation数据生成结果代码。

plugin的实现可以是一个类,使用时传入相关配置来创建一个实例,然后放到配置的plugins字段中,而plugin实例中最重要的方法是apply,该方法在webpack compiler安装插件时会被调用一次,apply接收webpack compiler对象实例的引用,你可以在compiler对象实例上注册各种事件钩子函数,来影响webpack的所有构件流程,以便完成更多其他的构建任务。

一个简单的plugin例子:

class BasicPlugin{
  // 在构造函数中获取用户给该插件传入的配置
  constructor(options) {
  
  }
  
  // webpack 会调用 BasicPlugin 实例的 apply 方法给插件传入 compiler 对象
  apply(compiler) {
    compiler.plugin('compilation', function(compilation) {
    
    })
  }
}

// 导出 Plugin
module.exports = BasicPlugin

Webpack启动后,在读取配置的过程中会先执行 new BasicPlugin(options) 初始化一个BasicPlugin获得其实例。在初始化 compiler 对象后,再调用 basicPlugin.apply(compiler)给插件实例传入compiler对象。插件实例在获取到compiler对象后,就可以通过compiler.plugin(事件名称,回调函数)监听到webpack广播出来的事件。并且可以通过compiler对象去操作Webpack

开发Plugin最主要的就是理解compilercompilation,它们是pluginwebpack之间的桥梁。这两者提供的各种hooksapi,则是开发plugin所必不可少的材料,通过compilercompilation的生命周期hooks,也可以更好的深入了解webpack的整个构建工作是如何进行的。

常见的loaderplugin

1. 常见loader

  1. file-loader: 文件加载
  2. url-loader: 文件加载,可以设置阈值,小于时把文件base64编码
  3. image-loader: 加载并压缩图片
  4. json-loader: webpack默认已经包含了
  5. babel-loaderES6+转成ES5
  6. ts-loader: 将ts转成js
  7. awesome-typescript-loader: 比ts-loader性能好一些 8. css-loader:处理@importurl这样的外部资源
  8. style-loader: 在head创建style标签把样式插入
  9. postcss-loader: 扩展css语法,使用postcss各种插件autoprefixer,cssnext,cssnano
  10. eslint-loader, tslint-loader:通过这两种检查代码,tslint不再维护
  11. vue-loader: 加载vue单文件组件
  12. i18n-loader: 国际化
  13. cache-loader:性能开销大的loader前添加,将结果缓存到磁盘
  14. svg-inline-loader:压缩后的svg注入代码
  15. source-map-loader: 加载source map文件,方便调试
  16. expose-loader: 暴露对象为全局变量 18. imports-loader、exports-loader等可以想模块注入变量或者提供导出模块功能
  17. raw-loader: 可以将文件以字符串的形式返回
  18. 校验测试: mocha-loader、jshint-loader、eslint-loader

2. 常见plugin

  1. ignore-plugin:忽略文件
  2. uglifyjs-webpack-plugin: 不支持ES6压缩(webpack4以前使用)
  3. terser-webpack-plugin:支持压缩ES6(webpack4)
  4. webpack-arallel-uglify-plugin: 多进程执行代码压缩,提升构建速度
  5. mini-css-extract-plugin: 分离样式文件,css提取为独立文件,支持按需加载
  6. serviceworker-webpack-plugin: 为网页应用增加离线缓存功能
  7. clean-webpack-plugin: 目录清理
  8. speed-measure-webpack-plugin: 可以看到每个LoaderPlugin执行耗时
  9. webpack内置UglifyJsPlugin,压缩和混淆代码
  10. webpack内置CommonsChunkPlugin,提高打包效率,将第三方库和业务代码分开打包。
  11. ProviderPlugin: 自动加载模块,代替requireimport
  12. html-webpack-plugin可以根据模板自动生成html代码,并自动引用cssjs文件
  13. extract-text-webpack-pluginjs文件中引用的样式单独抽离成css文件
  14. DefinePlugin编译时配置全局变量,这对开发模式和发布模式的构建允许不同的行为非常有用
  15. HotModuleReplacementPlugin热更新
  16. DllPluginDllReferencePlugin相互配合,前者第三方包的构建,只构建业务代码,同事能解决Externals多次引用问题。DllReferencePlugin引用DllPlugin配置生成的manifest.json文件,manifest.json包含了依赖模块和module id的映射关系
  17. optimize-css-assets-webpack-plugin不同组件中重复的css可以快速去重
  18. webpack-bundle-analyzer 一个webpackbundle文件分析工具,将bundle文件可以交互缩放的treemap的形式展示
  19. compression-webpack-plugin 生产环境可以采用gzip压缩jscss
  20. happypack: 通过多进程模式,来加速代码构建