Fork me on GitHub

webpack详解~~~

webpack详解~~~

手写loader

loader编写原则

  • 单一原则: 每个 Loader 只做一件事;
  • 链式调用: Webpack 会按顺序链式调用每个 Loader
  • 统一原则: 遵循 Webpack 制定的设计规则和结构,输入与输出均为字符串,各个 Loader 完全独立,即插即用;

在日常开发环境中,为了方便调试我们往往会加入许多console打印。但是我们不希望在生产环境中存在打印的值。那么这里我们自己实现一个loader去除代码中的console

知识点普及之ASTAST通俗的来说,假设我们有一个文件a.js,我们对a.js里面的1000行进行一些操作处理,比如为所有的await 增加try catch,以及其他操作,但是a.js里面的代码本质上来说就是一堆字符串。那我们怎么办呢,那就是转换为带标记信息的对象(抽象语法树)我们方便进行增删改查。这个带标记的对象(抽象语法树)就是AST。这里推荐一篇不错的AST文章 AST快速入门

npm i -D @babel/parser @babel/traverse @babel/generator @babel/types
复制代码
  • @babel/parser 将源代码解析成 AST
  • @babel/traverse 对AST节点进行递归遍历,生成一个便于操作、转换的path对象
  • @babel/generator 将AST解码生成js代码
  • @babel/types通过该模块对具体的AST节点进行进行增、删、改、查

 

为什么需要loader?

 webpack 实际上只能处理js文件,那么对于除了js文件的其他类型的文件 比如 css sass 等。。我们不能直接用webpack来处理。

 我们需要一个翻译员(loader)来帮我们的文件处理一下。有时候我们不只需要一个翻译员来工作,比如要把文言文翻译成外语,首先要转换成白话文,然后转换为外语。

 Loader就像一个翻译员,能将源文件经过转化后输出新的结果,并且一个文件还可以链式的经过多个翻译员翻译。

以scss文件为例:

 先将scss源代码交给sass-loader,将scss转换成css;
 将sass-loader输出的css提交给css-loader处理,找出css中依赖的资源,压缩css;
 将css-loader输出的css提交给style-loader处理,转换成通过脚本加载的javascript代码。

 最终的结果一定是javascript代码。

编写loader的原则

  职责单一: 一个loader只做一件事情。优点:容易维护且能够链式调用
  模块化:
保证输出模块化,loader生成的模块与普通块遵循的相同的设计原则
  无状态:
确保loader在不同模块转换之间,不保存状态。每次运行都应该独立于其他编译模块以及相同模块之前的编译结果。

 

手写一个webpack Loader

3.2 手写webpack plugin

在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过Webpack提供的API改变输出结果。通俗来说:一盘美味的 盐豆炒鸡蛋 需要经历烧油 炒制 调味到最后的装盘等过程,而plugin相当于可以监控每个环节并进行操作,比如可以写一个少放胡椒粉plugin,监控webpack暴露出的生命周期事件(调味),在调味的时候执行少放胡椒粉操作。那么它与loader的区别是什么呢?上面我们也提到了loader的单一原则,loader只能一件事,比如说less-loader,只能解析less文件,plugin则是针对整个流程执行广泛的任务。

一个基本的plugin插件结构如下

class firstPlugin {
  constructor (options) {
    console.log('firstPlugin options', options)
  }
  apply (compiler) {
    compiler.plugin('done', compilation => {
      console.log('firstPlugin')
    ))
  }
}

module.exports = firstPlugin
复制代码

compiler 、compilation是什么?

  • compiler 对象包含了Webpack 环境所有的的配置信息。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 optionsloader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。
  • compilation对象包含了当前的模块资源、编译生成资源、变化的文件等。当运行webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

compiler和 compilation的区别在于 

  • compiler代表了整个webpack从启动到关闭的生命周期,而compilation 只是代表了一次新的编译过程

  • compiler和compilation暴露出许多钩子,我们可以根据实际需求的场景进行自定义处理

compiler钩子文档

compilation钩子文档

下面我们手动开发一个简单的需求,在生成打包文件之前自动生成一个关于打包出文件的大小信息

新建一个webpack-firstPlugin.js

class firstPlugin{
  constructor(options){
    this.options = options
  }
  apply(compiler){
    compiler.plugin('emit',(compilation,callback)=>{
      let str = ''
      for (let filename in compilation.assets){
        str += `文件:${filename}  大小${compilation.assets[filename]['size']()}\n`
      }
      // 通过compilation.assets可以获取打包后静态资源信息,同样也可以写入资源
      compilation.assets['fileSize.md'] = {
        source:function(){
          return str
        },
        size:function(){
          return str.length
        }
      }
      callback()
    })
  }
}
module.exports = firstPlugin
复制代码

如何使用

const path = require('path')
const firstPlugin = require('webpack-firstPlugin.js')
module.exports = {
    // 省略其他代码
    plugins:[
        new firstPlugin()
    ]
}
复制代码

执行 npm run build即可看到在dist文件夹中生成了一个包含打包文件信息的fileSize.md

上面两个loaderplugin案例只是一个引导,实际开发需求中的loaderplugin要考虑的方面很多,建议大家自己多动手尝试一下。

附上官网 如何编写一个plugin

 

posted @ 2021-06-08 15:50  Kaicy  阅读(103)  评论(0编辑  收藏  举报