阅读 156

loader和plugin专题

plugin

原理

webpack插件是一个具有apply方法的JS对象,apply方法会被webpack compiler调用,并且在整个编译生命周期都可以访问compiler对象。

原理:通过在生命周期的钩子中挂载函数,来实现功能扩展。

生命周期就是整个生命过程中的关键节点。

自定义plugin

钩子是提前在可能增加功能的地方,埋好(预设)一个函数,生命周期的函数。

官网钩子40-50个,以下是几个关键钩子

钩子描述类型
environment环境准备好SyncHook
compile编译开始SyncHook
compilation编译结束SyncHook
emit打包资源到output之前SyncSeriesHook
afterEmit打包资源到output之后SyncSeriesHook
done打包完成SyncHook
const pluginName = 'MyWebpackPlugin';
class MyWebpackPlugin {
  // compiler 对象是 webpack 编译器对象
  apply(compiler){
    // hook中的tap函数的第一个参数是插件名称
    compiler.hooks.run.tap(pluginName, compilation => {
      // webpack配置应用了插件后就会执行到此处
      console.log("webpack 构建过程开始!")
    })
  }
}
复制代码

plugin的特性

  • 是一个独立的模块
  • 模块对外暴露一个 js 函数
  • 函数的原型 (prototype) 上定义了一个注入 compiler 对象的 apply 方法
  • apply 函数中需要有通过 compiler 对象挂载的 webpack 事件钩子,钩子的回调中能拿到当前编译的 compilation 对象,如果是异步编译插件的话可以拿到回调 callback
  • 完成自定义子编译流程并处理 complition 对象的内部数据
  • 如果异步编译插件的话,数据处理完成后执行 callback 回调

loader

loader的特性

  • loader本质上是一个ESM模块,它导出一个函数,在函数中对打包资源进行转换

  • loader必须返回一段js代码,这样eval才能执行,否则灰报错,多个loader,保证最后面一个返回js代码就行。

loader执行顺序

当配置多个loader时,loader的执行顺序时从右往左,右边的执行结果作为参数传到左边。

less-loader把less转化成css,传给css-loader,css-loader把结果给style-loader,style-loader返回javascript代码字符串。
{
    test:/\.less$/,
    use:[
        'style-loader','css-loader','less-loader'
    ]
}
复制代码

数组对象形式,这么写也可以说是默认从下到上

module: {
    rules: [
      {
        test: /\.less$/,
        use: 'style-loader'
      },
     {
        test: /\.less$/,
        use: 'css-loader'
      },
     {
        test: /\.less$/,
        use: 'less-loader'
      }
    ]
  }
复制代码

如何保证各个loader按照预想方式工作?

可以使用 enforce 强制执行 loader 的作用顺序,pre 代表在所有正常 loader 之前执行,post 是所有 loader 之后执行。(inline 官方不推荐使用)

可以通过一个 enforce 属性,默认有以下几个值

  1. pre 优先处理
  2. normal 正常处理(默认)
  3. inline 其次处理
  4. post 最后处理

enforce的使用方法:

module: {
    rules: [
      {
        test: /\.less$/,
        use: 'less-loader',
        enforce: 'pre'
      },
     {
        test: /\.less$/,
        use: 'css-loader'
      },
     {
        test: /\.less$/,
        use: 'style-loader',
        enforce: 'post'
      }
    ]
  },
复制代码

自定义loader

loader-utils 可以获取loader的配置项option

比如:申明一个读取markdown文件内容的loader。

cosnt {getOptions} = require('loader-utils')
const marked = require('marked')
module.exports = function(source){
    
    //获取loader配置选项
    const options =  getOptions(this)
    
    //对输入内容进行处理
    const html = marked(source)
    //返回给下一个loader处理,如果不用给下一个loader处理需要保证返回js代码
    return html
    
}
复制代码
比如 :css-loader 把css解析成webpack识别的模块
module.exports = function(source) {
  return `module.exports=${source}`;
}
复制代码
  • 当定义一个loader时,这个loader函数只有一个参数,参数是包含文件内容的字符串。
  • 同步loader可以返回一个代表模块转化后的简单的值
  • loader的返回值是javascript代码字符串或者是Buffer

小结

配置

  • 条件匹配: 通过test、include、exclude三个配置来命中Loader要应用的规则文件。
  • 应用规则: 对选中后的文件通过use配置项来应用loader,可以应用一个loader或者按照从后往前的顺序应用一组loader。同时还可以分别给loader传入参数。
  • 重置顺序: 一组loader的执行顺序默认是从从右往左执行,通过exforce选项可以让其中一个loader的执行顺序放到最前或者是最后。

编写原则

  • 单一职责。每个loader只负责一件事情。
  • 使用链式调用,确保loader的依赖关系的正确。
  • 无状态性,确保loader在不同模块转换之间不保存状态。每次运行都应该独立于其他编译模块以及相同模块之前的编译结果。

loader和plugin的区别

  1. 功能及特点
  • loader:对文件进行转换成webpack能够识别的文件,从右往左执行

  • plugin:插件,监听文件,在webpack打包的时候对文件进行处理,目的在于解决loader无法实现的其他事

  • loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。因为 Webpack 只认识JavaScript,所以Loader就成了翻译官,对其他类型的资源进行转译的预处理工作。

loader描述了webpack如何处理非javascript模块,并且在build中引入这些依赖。loader可以将文件从不同的语言(如TypeScript)转换为JavaScript,或者将内联图像转换为data URL。比如说:CSS-Loader,Style-Loader等。

  • plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
  1. 配置
  • loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。
  • plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。

常用loader和plugin

打包CSS

  • style-loader :把写入js的样式代码插入到 index.html文件中
  • css-loader :仅仅只是把样式代码写入 js
  • postcss-loader 前缀 postcss.config.js require('autoprefixer')
  • less-loader
  • stylelint-webpack-plugin : css代码格式校验 还得安装stylelint stylelint-config-standard【大厂基本是使用的规范】
  • optimize-css-assets-webpack-plugin /css-minimizer-webpack-plugin css压缩,不同组件中重复的css可以快速去重
  • sass-loader:加载SASS / SCSS 文件并将其编译为 CSS
  • mini-css-extract-plugin:打包css文件。之前打包的css是把css样式打包到js文件里面,使用这个插件可以把样式抽离成一个css文件;这个插件要同时使用他的loader和plugin才行
require('./index.css')
// 这个插件要同时使用他的loader和plugin才行
plugins:[
  new MiniCssExtractPlugin({
    filename: 'css/main.css'
  }),
],
 module: {
   rules: [
      {
        test: /\.css$/,
        loader: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
 }
 踩坑在于,把style-loader和miniCss.loader一起使用了,导致一直打包不成功,报错document is not found
复制代码

打包JS

  • babel-loader babel-core @babel/preset-env

@babel/preset-env只能转译基本语法(promise就不能转换)

@babel/polyfill 转换所有js新语法 引入文件太大,增加垃圾代码 core-js 按需转译JS新语法,这个最好

  • eslint-webpack-plugin JS代码格式校验 eslint eslint-config-airbnb-base eslint-webpack-import

打包图片

  • file-loader :将要加载的文件复制到指定目录;生成请求文件资源URL,是按需导入,文件中没有引入不会被处理。它是将用到的图片复制到输出目录,过滤掉不用的图片。
{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'file-loader',
    query: {
      name: '[name].[ext]',
      outputPath: 'static/img/',
      publicPath: '/dist/static/img/'
    }
  }
name指定文件名
outputPath指定文件的输出目录,此处应为相对于webpack输出目录的相对路径。
publicPath指定请求文件时的URL。如上面的配置,生成的URL为/dist/static/img/${name}

注意
outputPath, publicPath一定要以/结尾
复制代码

注:mini-css-extract-plugin 需要额外处理css中的图片路径(例如:背景图片加载失败),不然会加载失败,具体看MiniCssExtractPlugin.loader

  • url-loader: file-loader的升级版,如果图片小于配置大小,会转成base64字符串,减少图片的请求次数,如设置设置限制大小8192,也就是8M【使用url-loader,不能删除file-loader, file-loader是基本功能,url-loader是加强功能,这句话要进一步验证】

  • html-loader:将html导出为字符串。在导出的过程中,它可以把html中所有跟资源有关的内容做处理,所以可以解决图片引入的问题。 它会把图片转化成require或者import的形式。音频、视频等资源都可以被处理。

处理html中图片加载的问题,如果在html中直接引入图片,不使用html-loader不会对图片路径做任何处理,可以使用html-loader处理。

尽管配置了html-loader,html中的路径图片得到了处理,但是不能正常读出图片,是因为 url-loader默认采用ES Modules规范解析,但是html-loader引入图片使的是CommonJS规范。 解决:关闭url-loader默认的ES Modules规范,强制url-loader使用CommonJS规范进行打包

esModule:false
复制代码

webpack4中只需要url-loader配置esModule:false

webpack5需要html-loader和url-loader都配置esModule:false

html-loader和html-webpack-plugin的冲突:

原因:htmlWebpackPlugin会检测目标文件是否已经有loader处理,如果有

其他loader处理,htmlWebpackPlugin不再使用lodash.template去处理ejs语法。

html-loader 如果是普通的html,就按照html-loader处理,否则html都改成ejs语法。

html-loader 可以处理静态的模板,而且模板里没有使用ejs语法语法,可以处理图片加载问题。如果使用了ejs语法,可以使用html-webpack-

  • image-webpack-loader:使用图片压缩能极大的减少包的大小

打包字体

  • file-loader 打包字体

不满足url-loader的条件就使用file-loader

其他

  • DefinePlugin(webpack内置插件):定义环境变量,这对开发模式和发布模式的构建允许不同的行为非常有用。
  • copy-webpack-plugin :不需要处理的其他文件,可以直接复制到输出目录, 拷贝资源插件
  • clean-webpack-plugin:清理指定目录下指定的文件 每次打包之前,先删除历史文件
  • html-webpack-plugin : 自动生成html (filename,title,template)
  • compression-webpack-plugin:压缩文件,生产环境可采用gzip压缩JS和CSS
new CompressionPlugin({    //打包文件为giz格式
    filename: '[path].gz[query]', //目标资源名称。[file] 会被替换成原资源。[path] 会被替换成原资源路径,[query] 替换成原查询字符串
    algorithm: 'gzip',//算法
    test: new RegExp('\\.(js|css)$'),
    threshold: 10240,//只处理比这个值大的资源。按字节计算
    minRatio: 0.8//只有压缩率比这个值小的资源才会被处理
   })
复制代码
  • vconsole-webpack-plugin:调试

  • extract-text-webpack-plugin:将js文件中引用的样式单独抽离成css文件,防止将样式打包在js中引起页面样式加载错乱的现象

  • ProvidePlugin: 自动加载模块,而不必到处 import 或 require

要自动加载 jquery,我们可以将两个变量都指向对应的 node 模块
new webpack.ProvidePlugin({
  $: 'jquery',
  jQuery: 'jquery'
})
复制代码
  • SplitChunksPlugin: 拆包
splitChunks: {
  chunks: 'async',
  minSize: 20000,
  minRemainingSize: 0,
  maxSize: 0,
  minChunks: 1,
  maxAsyncRequests: 30,
  maxInitialRequests: 30,
  automaticNameDelimiter: '~',
  enforceSizeThreshold: 50000,
  cacheGroups: {
    defaultVendors: {
      test: /[\\/]node_modules[\\/]/,
      priority: -10
    },
    default: {
      minChunks: 2,
      priority: -20,
      reuseExistingChunk: true
    }
  }
}
复制代码
  • uglifyjs-webpack-plugin:基于UglifyJS压缩代码
new UglifyJsPlugin()
复制代码
var webpackConfig = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
  plugins: [new HtmlWebpackPlugin()]
};
复制代码
  • CommonsChunkPlugin(webpack内置插件):提高打包效率,将第三方库和业务代码分开打包。

  • babel-plugin-transform-runtime:减少冗余代码

注意:

为什么不用脚手架:如果你直接使用了vue-cli,wepback从3升级到4,会碰到extract-text-webpack-plugin还没有webpack4版本,会报错,使用npm install extract-text-webpack-plugin@next解决

避免不了将生态相关的插件更新到新版,这个过程中可能会遇到很多坑:比如CommonsChunkPlugin 已经在webpack4中移除

url-loader 和 file-loader的区别

url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。

file-loader 默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名。

import img from './file.png' 生成文件 file.png,输出到输出目录并返回 public URL。 "/public/path/0dcbbaa7013869e351f.png"

test: /\.(jpe?g|png|gif)$/,
use: [
  {
    loader: 'url-loader',
    options: {
      esModule: false, // 不加的话会有这种情况 img属性src="[object Module]"
      limit: 1024 * 100, // 当大于100kb时候,将文件打包到publicPath中 
      outputPath: 'images', // 将文件打包到哪里
      publicPath: 'images/',
      name: '[name].[hash:8].[ext]'
    }
  }
]

options中的publicPath+name=html和css引用路径的全写,那也就意味着,如果publicPath是../images/ 会导致页面上的引用路径出错,

而如果publicPath改成了images/,那么css引用的路径会出错,(好像不对?)

我的项目中设置:

outputPath: 'img',
publicPath: '/img',    
复制代码

这种打包方式会存在一定的缺陷:打包之后,每个图片在加载时,都将会发送一个http请求,当页面图片过多,会严重拖慢网页加载速度。这种情况下,我们可以选择用url-loader进行打包,通过配置规则,让较小的图片打包成base64的形式存放在打包后的js中,不再需要单独发送http请求加载图片。

www.jianshu.com/p/dc7226ac6…
www.yuque.com/yijiangxili…

segmentfault.com/a/119000001…

文章分类
前端
文章标签