zoukankan      html  css  js  c++  java
  • 【前端】Webpack 进阶

    【webpack进阶】可视化展示webpack内部插件与钩子关系 - 掘金

    【钩子的类函数】

    通过Tapable,可以快速创建各类钩子。以下是各种钩子的类函数:

    const {
    	SyncHook,
    	SyncBailHook,
    	SyncWaterfallHook,
    	SyncLoopHook,
    	AsyncParallelHook,
    	AsyncParallelBailHook,
    	AsyncSeriesHook,
    	AsyncSeriesBailHook,
    	AsyncSeriesWaterfallHook
    } = require("tapable");
    

    然后,webpack中的插件会将所需执行的函数通过 .tap().tapAsync().tapPromise()等方法注册到对应钩子上。这样,webpack调用相应钩子时,插件中的函数就会自动执行。

    那么,还有一个问题:webpack是如何调用插件,将插件中的方法在编译阶段注册到钩子上的呢?

    对于这个问题,webpack规定每个插件的实例,必须有一个.apply()方法,webpack打包前会调用所有插件的.apply()方法,插件可以在该方法中进行钩子的注册。

    在webpack的lib/webpack.js中,有如下代码:

    if (options.plugins && Array.isArray(options.plugins)) {
        for (const plugin of options.plugins) {
            plugin.apply(compiler);
        }
    }
    复制代码

    上面这段代码会从webpack配置的plugins字段中取出所有插件的实例,然后调用其.apply()方法,并将Compiler的实例作为参数传入。这就是为什么webpack要求我们所有插件都需要提供.apply()方法,并在其中进行钩子的注册。

    注意,和.call()一样,这里的.apply()也不是js的原生方法。你会在源码中看到许多.call().apply(),但它们基本都不是你认识的那个方法。

    【钩子分类】

    钩子数量众多。webpack内部的钩子非常多,数量达到了180+,类型也五花八门。除了官网列出的compilercompilation中那些常用的钩子,还存在着众多其他可以使用的钩子。有些有用的钩子你可能无从知晓,例如我最近用到的localVarsrequireExtensions等钩子。

    模块/插件与钩子的关系主要分为三类:

    • 模块/插件「创建」钩子,如this.hooks.say = new SyncHook()
    • 模块/插件将方法「注册」到钩子上,如obj.hooks.say.tap('one', () => {...});
    • 模块/插件通过「调用」来触发钩子事件,如obj.hooks.say.call()

     


    【webpack进阶】前端运行时的模块化设计与实现
     

    【模块包装】

    浏览器原生实际是不支持所谓的CommonJS或ESM模块化规范的。那么webpack是如何在打包出的代码中实现模块化的呢?

    当我们写一个Node(JavaScript)模块时,模块里的modulerequire__filename等这些变量是哪来的?如果你看过Node loader.js 部分源码,应该就大致能理解:

    Module.wrap = function(script) {
      return Module.wrapper[0] + script + Module.wrapper[1];
    };
    
    Module.wrapper = [
      '(function (exports, require, module, __filename, __dirname) { ',
      '
    });'
    ];
    复制代码

    Node会自动将每个模块进行包装(wrap),将其变为一个function。

    modulerequire__filename这些变量都是哪来的? —— 它们会被作为function的参数在模块编译执行时注入进来。

    【模块暂存】

    仔细对比webpack与Node,你会发现在__webpack_require__中有一个重要的区别:

    在webpack中不存在像Node一样调用._compile()这种方法的过程。即不会像Node那样,对一个未载入缓存的模块,通过「读取模块路径 -> 编译模块代码 -> 执行模块」来载入模块。为什么呢?

    这是因为,Node作为服务端语言,模块都是本地文件,加载时延低,可同步阻塞进行模块文件寻址、读取、编译和执行,这些过程在模块require的时候再“按需”执行即可;而webpack运行在客户端(浏览器),显然不能在需要时(即执行__webpack_require__时)再通过网络加载js文件,并同步地等待加载完成后再返回__webpack_require__。这种网络时延,显然不能满足“同步依赖”的要求。

    把同步依赖的模块先“注册”到内存中(模块暂存),等到require时,再执行该模块、缓存模块对象、返回对应的exports。而webpack中,这个所谓的内存就是modules对象。

    【异步依赖】

    webpack支持使用动态模块引入的语法(代码拆分),例如:dynamic import和早期的require.ensure,这种方式与使用CommonJS的require和ESM的import最重要的区别在于,该类方法会异步(或者说按需)加载依赖。

    异步依赖的核心方法就是__webpack_require__.e

    该方法首先会根据chunkId在installChunks中判断该chunk是否正在加载或已经被加载;如果没有则会创建一个promise,将其保存在installChunks中,并通过jsonpScriptSrc()方法获取文件路径,通过sciript标签加载,最后返回该promise。

    【Resolve】

    webpackJsonpCallback()方法中,有一段代码就是根据chunkIds的数组,检查并更新chunk的加载状态:

    上面的代码先根据模块注册时的chunkId,取出installedChunks对应的所有loading中的chunk,最后将这些chunk的promise进行resolve操作。



  • 相关阅读:
    Ajax技术
    java web中filter分析
    Async分析
    解释session
    XML相关知识
    开学第一课
    svn
    spa单页面应用(angular)
    angular
    webpack认识
  • 原文地址:https://www.cnblogs.com/cx2016/p/12926184.html
Copyright © 2011-2022 走看看