原文地址

编写 loader 和 plugins

github

一、loader

1.loader 介绍

loader 是什么

loader 其实是一个函数,对匹配到的内容进行转换,将转换后的后果返回。

loader 作用

webpackloader 就像是一位翻译官。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 的三种应用形式

    1. webpack.config.js 中配置

      module.exports = {  module:{   rules:[{           test:/\.css$/,           use:['css-loader'],           // use:{loader:'css-loader',options:{}}       }   ]  }}
    1. 通过命令行的参数形式

      webpack --module-bind 'css=css-loader'
    1. 通过内联应用

      import txt from 'css-loader!./file.css';

2. 编写一个 loader

思路:后面咱们说过 1.loader 是一个函数;2.对匹配到的内容进行转换;3.再将转换内容返回,依照这个思路咱们能够编写一个最简略的loader
// 在 ./loader/replaceLoader.js 创立一个替换字符串的 loadermodule.exports = function(source) {    return source.replace('a', 'b')}// 在webpack.config.js 应用 本人写的loadermodule.exports = {    module:{        rules:[{            test:"/\.js$/",            use:[{                    loader: path.resolve(__dirname, './loader/replaceLoader.js')                    options:{                        name: '林一一'                       }                }            ]        }]    }}// 或者应用 replaceLoadermodule.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 的思路

  1. loader 是一个导出函数,有返回值,能够借助第三方模块和Node api 实现。
  2. loader 能够应用 loader-utils 接管到options 中传递过去的参数
  3. loader 的异步编写须要显示的申明 const callback = this.async() 表明异步。
  4. loader 如果须要解决二进制文件也须要申明exports.raw = true
  5. loader 的容许后果会被webpack缓存,如果须要敞开webpack的缓存后果须要申明this.cacheable(false)
  6. 编写后的本地loader 能够借助 Npm linkresolveLoader 导入。

二、webpack 的构建流程

再讲 plugins 之前须要先分明 webpack 的构建流程是怎么的
  1. 初始化参数。从配置文件和 shell 语句中合并的参数
  2. 开始编译。将上一步失去的参数初始化成 complier对象,加载所有的导入插件,执行对象的 run 办法开始执行编译;
  3. 确定入口。从配置的 entry 入口找出所有的入口文件。
  4. 编译模块。依据入口文件的依赖,调用所有配置的loader进行转换。
  5. 实现模块编译并输入。依据入口文件之间的依赖关系,造成一个个代码块 chunk
  6. 输入实现。将造成的代码块 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 的思路

  1. 编写一个 class 类 。
  2. 在类中定义一个 apply 办法。
  3. 在利用办法apply()中指定挂载的 webpack 事件钩子complier.hooks.
  4. 解决 webpack 外部实例的特定数据。
  5. 性能实现后调用 webpack 提供的回调。

四、面试题

1. loader 和 plugin 的区别

  1. loader 是一个函数,用来匹配解决某一个特定的模块,将接管到的内容进行转换后返回。在webpack 中操作文件,充当文件转换器的角色。在 modules.rules 中配置。
  2. plugin 是一个插件,不间接操作文件,基于事件流框架 Tapable 实现,plugin 通过钩子能够波及到 webpack 的整一个事件流程。也就是说 plugin 能够通过监听这些生命周期的钩子在适合的机会应用webpack 提供的API 做一些事件。在plugins中配置插件

2. loader 的编写思路

参上

3. plugin 的编写思路

参上

4. complier 和 compilation 区别

  1. complier 对象裸露了 webpack 整一个生命周期相干的钩子,是 webpack 初始化的参数的产物,蕴含options, entry, plugins等属性能够简略的了解为webpack的一个实例。
  2. compilation 对象是 complier 的实例,是每一次 webpack 构建过程中的生命周期对象。每一个文件发生变化后都能生成一个complition对象。
    总结:两个对象都有本人的生命周期钩子,compilation 对象 负责的是粒度更小的生命周期钩子。compiler对象是webpack整一个整个生命周期钩子的对象。

参考

webpack之loader和plugin简介

webpack 构建流程

webpack loader和plugin编写

深刻Webpack-编写Loader