zoukankan      html  css  js  c++  java
  • webpack 之 loader 和 plugin 简介

    webpack是一个模块打包器(module bundler),提供了一个核心,核心提供了很多开箱即用的功能,同时它可以用loader和plugin来扩展。webpack本身结构精巧,基于tapable的插件架构,扩展性强,众多的loader或者plugin让webpack显得很复杂。
    webpack常用配置包括:devtool、entry、 output、module、resolve、plugins、externals等,本文主要介绍下webpack常用的loader和plugin
     

    一、loader

           1.1、loader的使用方式

    webpack允许我们使用loader来处理文件,loader是一个导出为function的node模块。可以将匹配到的文件进行一次转换,同时loader可以链式传递。

    一般loader的使用方式分为三种:
    1:在配置文件webpack.config.js中配置

    module.exports = {
      module: {
        rules: [
          {
            test: /.txt$/,
            use: 'raw-loader'
          }
        ]
      }
    }

    2:通过命令行参数方式

    webpack --module-bind 'txt=raw-loader'

    3:通过内联使用

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

    1.2、webpack常用的loader

    • 样式:style-loader、css-loader、less-loader、sass-loader等
    • 文件:raw-loader、file-loader 、url-loader等
    • 编译:babel-loader、coffee-loader 、ts-loader等
    • 校验测试:mocha-loader、jshint-loader 、eslint-loader等

    比如下面配置,可以匹配.scss的文件,分别经过sass-loader、css-loader、style-loader的处理。
    sass-loader转化sass为css文件,并且包一层module.exports成为一个js module。style-loader将创建一个style标签将css文件嵌入到html中。css-loader则处理其中的@import和url()。

    module.exports = {
      module: {
        rules: [
            {
              test: /.scss$/,
              use:[
                  {loader:'style-loader'},
                  {loader:'css-loader',options:{sourceMap:true,modules:true}},
                  {loader:'sass-loader',options:{sourceMap:true}}
              ],
              exclude:/node_modules/
          }
        ]
      }
    }
    • vue-loader、coffee-loader、babel-loader等可以将特定文件格式转成js模块、将其他语言转化为js语言和编译下一代js语言
    • file-loader、url-loader等可以处理资源,file-loader可以复制和放置资源位置,并可以指定文件名模板,用hash命名更好利用缓存。
    • url-loader可以将小于配置limit大小的文件转换成内敛Data Url的方式,减少请求。
    • raw-loader可以将文件以字符串的形式返回
    • imports-loader、exports-loader等可以向模块注入变量或者提供导出模块功能,常见场景是:
      1:jquery插件注入$,imports-loader?$=jquery
      2:禁用AMD,imports-loader?define=false
      等同于:var $ = require("jquery") 和 var define = false;
    • expose-loader:暴露对象为全局变量

    1.3、如何写一个loader:官网介绍how to write a loader

    loader 是导出为一个函数的 node 模块。该函数在 loader 转换资源的时候调用。给定的函数将调用 loader API,并通过 this 上下文访问。

    下面是一个简单的raw-loader,它可以将文本类文件转成字符串到js文件中。其中this.cacheable、this.value等是loader的api,分别是将结果标记为可缓存和把值传递给下一个loader。

    module.exports = function(content) {
      this.cacheable && this.cacheable();
      this.value = content;
      return "module.exports = " + JSON.stringify(content);
    }

    二、plugin

    webpack的plugin比loader强大,通过钩子可以涉及整个构建流程,可以做一些在构建范围内的事情。

    2.1、webpack常用的plugin

    • 官网介绍plugins
    • 第三方插件awesome-webpack
    • 首先webpack内置UglifyJsPlugin,压缩和混淆代码。
    • webpack内置CommonsChunkPlugin,提高打包效率,将第三方库和业务代码分开打包。
    • ProvidePlugin:自动加载模块,代替require和import
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery'
      })
    • html-webpack-plugin可以根据模板自动生成html代码,并自动引用css和js文件
    • extract-text-webpack-plugin 将js文件中引用的样式单独抽离成css文件
    • DefinePlugin 编译时配置全局变量,这对开发模式和发布模式的构建允许不同的行为非常有用。
    new webpack.DefinePlugin({
      PRODUCTION: JSON.stringify(true),
      VERSION: JSON.stringify("5fa3b9"),
      BROWSER_SUPPORTS_HTML5: true,
      TWO: "1+1",
      "typeof window": JSON.stringify("object")
    })
    • HotModuleReplacementPlugin 热更新

      • 添加HotModuleReplacementPlugin
      • entry中添加 "webpack-dev-server/client?http://localhost:8080/",
      • entry中添加 "webpack/hot/dev-server"
      • (热更新还可以直接用webpack_dev_server --hot --inline,原理也是在entry中添加了上述代码)
    • webpack 内置的DllPluginDllReferencePlugin相互配合,前置第三方包的构建,只构建业务代码,同时能解决Externals多次引用问题。DllReferencePlugin引用DllPlugin配置生成的manifest.json文件,manifest.json包含了依赖模块和module id的映射关系

    • babili-webpack-plugin、transform-runtime 、transform-object-rest-spread
      • babili-webpack-plugin:构建在babel之上 why
      • transform-runtime :解决了babel在每个文件都插入了辅助代码,代码体积过大的问题。
      • transform-object-rest-spread:
        Transform rest properties for object destructuring assignment and spread properties for object literals
        为对象字面量添加解构赋值和spread属性
    • optimize-css-assets-webpack-plugin 不同组件中重复的css可以快速去重

    • webpack-bundle-analyzer 一个webpack的bundle文件分析工具,将bundle文件以可交互缩放的treemap的形式展示。
    • compression-webpack-plugin 生产环境可采用gzip压缩JS和CSS
    • happypack:通过多进程模型,来加速代码构建

    const os = require('os');
        let HappyPack = require('happypack');
        let happyThreadPool = HappyPack.ThreadPool({size: os.cpus().length});
        exports.plugins = [
          new HappyPack({
            id: 'jsx',
            threadPool: happyThreadPool,
            loaders: [ 'babel-loader' ]
          }),
    
          new HappyPack({
            id: 'coffeescripts',
            threadPool: happyThreadPool,
            loaders: [ 'coffee-loader' ]
          })
        ];
    
        exports.module.loaders = [
          {
            test: /.js$/,
            loaders: [ 'happypack/loader?id=jsx' ]
          },
          {
            test: /.coffee$/,
            loaders: [ 'happypack/loader?id=coffeescripts' ]
          },
        ]

    2.2、写一个webpack插件:

    官网介绍:how to write a plugin

    主要的步骤如下:
    • 编写一个JavaScript命名函数。
    • 在它的原型上定义一个apply方法。
    • 指定挂载的webpack事件钩子。
    • 处理webpack内部实例的特定数据。
    • 功能完成后调用webpack提供的回调。
    // 一个 JavaScript 命名函数。
    function MyExampleWebpackPlugin() {
    
    };
    
    // 在插件函数的 prototype 上定义一个 `apply` 方法。
    MyExampleWebpackPlugin.prototype.apply = function(compiler) {
      // 指定一个挂载到 webpack 自身的事件钩子。
      compiler.plugin('webpacksEventHook', function(compilation /* 处理 webpack 内部实例的特定数据。*/, callback) {
        console.log("This is an example plugin!!!");
    
        // 功能完成后调用 webpack 提供的回调。
        callback();
      });
    };

    Compiler 和 Compilation

    编写插件之前要理解compiler和compilation两个对象,以及webpack生命周期的各个阶段和钩子,plugin比loader强大,通过plugin你可以访问compliler和compilation过程,通过钩子拦截webpack的执行。

    在插件开发中最重要的两个资源就是 compiler 和 compilation 对象。理解它们的角色是扩展 webpack 引擎重要的第一步。 

      • compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。

      • compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

    基本插件架构

    插件是由「具有 apply 方法的 prototype 对象」所实例化出来的。这个 apply 方法在安装插件时,会被 webpack compiler 调用一次。apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象。一个简单的插件结构如下:

    function HelloWorldPlugin(options) {
      // 使用 options 设置插件实例……
    }
    
    HelloWorldPlugin.prototype.apply = function(compiler) {
      compiler.plugin('done', function() {
        console.log('Hello World!');
      });
    };
    
    module.exports = HelloWorldPlugin;

    然后,要安装这个插件,只需要在你的 webpack 配置的 plugin 数组中添加一个实例:

    var HelloWorldPlugin = require('hello-world');
    
    var webpackConfig = {
      // ... 这里是其他配置 ...
      plugins: [
        new HelloWorldPlugin({options: true})
      ]
    };

    compiler 钩子:https://www.webpackjs.com/api/compiler-hooks/

    compilation 钩子:https://www.webpackjs.com/api/compilation-hooks/

    2.3、webpack 插件分析

    首先介绍webpack源码分析方法

    • node --inspect-brk ./node_modules/webpack/bin/webpack.js --config ./webpack.config.js
    • chrome输入 chrome://inspect/
    1. 主要的流程是:

     2.webpack构建的主要钩子:


     3.Tapable

    compiler和compilation都继承于Tapable
    webpack的插件是基于Tapable的,Tapable允许你添加和应用插件到javascript模块中,类似于 NodeJS的EventEmitter,可以被继承和mixin到其他模块中,详情见官网Tapable
    其中关键的方法是

    • plugin(name:string, handler:function)
    • apply(...pluginInstances: (AnyPlugin|function)[])
    • applyPlugins*(name:string, ...)
    • mixin(pt: Object)
      tapable主要负责处理事件,采用的是发布订阅模式,apply相当于trigger,plugin相当于addEventListener
    Tapable.prototype.plugin = function plugin(name, fn) {
        if(Array.isArray(name)) {
            name.forEach(function(name) {
                this.plugin(name, fn);
            }, this);
            return;
        }
        if(!this._plugins[name]) this._plugins[name] = [fn];
        else this._plugins[name].push(fn);
    };

    plugin方法将插件对应的方法加入一个数组中、注册到事件(name)上,等待apply的时候串行调用/触发

    Compilation中做了很多事情,处理编译过程。所对应的方法,如addEntry ,buildModule,processModuleDependencies,createChunkAssets,seal等

    转自:https://juejin.cn/post/6844903489458405390
  • 相关阅读:
    CF1343E-Weights Distributing (最短路)
    科大讯飞杯-日期小助手(补)
    网络15软工个人作业5——软件工程总结
    软工网络15个人作业4——alpha阶段个人总结
    软件工程网络15个人作业3——案例分析
    软工网络15结对编程练习
    软工网络15个人阅读作业2——提问题
    软件工程网络15个人阅读作业1(201521123052 蓝锦明)
    Java课程设计 购物车系统(个人博客) 201521123052 蓝锦明
    201521123052 《Java程序设计》 第14周学习总结
  • 原文地址:https://www.cnblogs.com/vickylinj/p/14243518.html
Copyright © 2011-2022 走看看