资讯专栏INFORMATION COLUMN

webpack的编译&构建

roland_reed / 2594人阅读

摘要:的编译构建上一篇文章详解中介绍了基于事件流编程,是个高度的插件集合,整体介绍了的编译流程。本文将多带带聊一聊最核心的部分,编译构建。的编译重要的构建节点的构建中总会经历如下几个事件节点。

webpack的编译&构建

上一篇文章webpack详解中介绍了webpack基于事件流编程,是个高度的插件集合,整体介绍了webpack 的编译流程。本文将多带带聊一聊最核心的部分,编译&构建。

webpack的编译 重要的构建节点

webpack的构建中总会经历如下几个事件节点。

before-run 清除缓存

run 注册缓存数据钩子

compile 开始编译

make 从入口分析依赖以及间接依赖模块,创建模块对象

build-module 模块构建

seal 构建结果封装, 不可再更改

after-compile 完成构建,缓存数据

emit 输出到dist目录

其中make是整个构建中最核心的部分编译,通过模块工厂函数创建模块,然后对模块进行编译。

在make钩子的编译


上图中提到的*ModuleFactory是指模块工厂函数,之所以会有模块工厂这样的函数,还要从webpack中entry的配置说起,在webpack的配置项中entry支持如下类型:

字符类型string

字符数组类型[string]

多页面对象key-value类型object { : string | [string] }

也支持一个函数,返回构建的入口(function: () => string | [string] | object { : string | [string] })

为了处理以后不同类型的入口模块,所以就需要个模块工厂来处理不同的入口模块类型。

singleEntry: string|object { : string }

multiEntry: [string]|object { : [string] }

dynamicEntry: (function: () => string | [string] | object { : string | [string] })

上图中为了简单说明构建的流程,就以最直接的singleEntry类型说起,对于此类入口模块,webpack均使用NormalModuleFactory来创建模块,这个创建的模块的类型叫NormalModule,在NormalModule中实现了模块的构建方法build,使用runLoaders对模块进行加载,然后利用进行解析,分析模块依赖,递归构建。

构建封装seal

到构建封装阶段时候,代码构建已经完毕,但是如何将这些代码按照依赖引用逻辑组织起来,当浏览器将你构建出来的代码加载到浏览器的时候,仍然能够正确执行。在webpack中通过Manifest记录各个模块的详细要点,通过Runtime来引导,加载执行模块代码,特别是异步加载。

Runtime

如上所述,我们这里只简略地介绍一下。runtime,以及伴随的 manifest 数据,主要是指:在浏览器运行时,webpack 用来连接模块化的应用程序的所有代码。runtime 包含:在模块交互时,连接模块所需的加载和解析逻辑。包括浏览器中的已加载模块的连接,以及懒加载模块的执行逻辑。

Manifest

那么,一旦你的应用程序中,形如 index.html 文件、一些 bundle 和各种资源加载到浏览器中,会发生什么?你精心安排的 /src 目录的文件结构现在已经不存在,所以 webpack 如何管理所有模块之间的交互呢?这就是 manifest 数据用途的由来……
当编译器(compiler)开始执行、解析和映射应用程序时,它会保留所有模块的详细要点。这个数据集合称为 "Manifest",当完成打包并发送到浏览器时,会在运行时通过 Manifest 来解析和加载模块。无论你选择哪种模块语法,那些 import 或 require 语句现在都已经转换为 webpack_require 方法,此方法指向模块标识符(module identifier)。通过使用 manifest 中的数据,runtime 将能够查询模块标识符,检索出背后对应的模块。

定义了一个立即执行函数,声明了__webpack_require__,对各种模块进行加载。

(function(modules) { // webpackBootstrap
    var installedModules = {}; // cache module
    function __webpack_require__(moduleId) { // 模块加载
        // Check if module is in cache
        if (installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }
        // Create a new module (and put it into the cache)
        var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };
        // Execute the module function
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
        // Flag the module as loaded
        module.l = true;
        // Return the exports of the module
        return module.exports;
    }

    // expose the modules object (__webpack_modules__)
    __webpack_require__.m = modules;

    // expose the module cache
    __webpack_require__.c = installedModules;

    // define getter function for harmony exports
    __webpack_require__.d = function(exports, name, getter) {
        if (!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, {
                configurable: false,
                enumerable: true,
                get: getter
            });
        }
    };

    // getDefaultExport function for compatibility with non-harmony modules
    __webpack_require__.n = function(module) {
        var getter = module && module.__esModule ?
            function getDefault() { return module["default"]; } :
            function getModuleExports() { return module; };
        __webpack_require__.d(getter, "a", getter);
        return getter;
    };

    // Object.prototype.hasOwnProperty.call
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

    // __webpack_public_path__
    __webpack_require__.p = "";

    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = 0);
})([/**modules*/])

上面提到的代码片段便是webpack构建后在浏览器中执行的引导代码。也就是上面提到的runtime。它是个立即执行函数,那么入参modules便是上面的Manifest,组织各个模块的依赖逻辑。

(function(modules){
    // ...
    // runtime
    function __webpack_require__(moduleId) {
        // 加载逻辑
    }
    // ...
})([function (module, exports, __webpack_require__) {

    var chunk1 = __webpack_require__(1);
    var chunk2 = __webpack_require__(2);
   
}, function (module, exports, __webpack_require__) {

    __webpack_require__(2);
    var chunk1 = 1;
    exports.chunk1 = chunk1;
}, function (module, exports) {

    var chunk2 = 1;
    exports.chunk2 = chunk2;
}])

上面说到了runtimemanifest就是在seal阶段注入的

class Compilation extends Tapable {
   
    seal(callback) {
        this.hooks.seal.call();
        // ...
        if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
            this.hooks.beforeChunkAssets.call();
            this.createChunkAssets();
        }
        // ...
    }
  
    createChunkAssets() {
        // ...
        for (let i = 0; i < this.chunks.length; i++) {
            const chunk = this.chunks[i];
            // ...
                const template = chunk.hasRuntime() 
                    ? this.mainTemplate
                    : this.chunkTemplate; // 根据是否有runTime选择模块,入口文件是true, 需要异步加载的文件则没有
                const manifest = template.getRenderManifest({ 
                    // 生成manifest
                    chunk,
                    hash: this.hash,
                    fullHash: this.fullHash,
                    outputOptions,
                    moduleTemplates: this.moduleTemplates,
                    dependencyTemplates: this.dependencyTemplates
                }); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
                // ...
    }
}

通过template最后将代码组织起来,上面看到的构建后的代码就是mainTemplate生成的。

写在最后

通过template生成最后代码,构建已经完成,接下来就是将代码输出到dist 目录。

最后

腾讯IVWEB团队的工程化解决方案feflow已经开源:Github主页:https://github.com/feflow/feflow

如果对您的团队或者项目有帮助,请给个Star支持一下哈~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93927.html

相关文章

  • 前端团队 Gulp &amp; Webpack 工作流 迁移记

    摘要:那时候所配置的任务监听匹配文件的变化自动刷新浏览器自动编译自动补全前缀多雪碧图合并拼图等等基于编译图片的任务,已经是完全满足我们的需求了。直至到后来在雪碧图的合并,多倍图的输出上,在上苦苦找寻不了比较完美的解决方案等等。 折腾 从 2015 到现在,短短的三年内,几乎每年折腾一下工作流的 更新换代 。从最早开始使用 Grunt 到 Gulp 再到 Webpack,再到 Rollup,...

    Baaaan 评论0 收藏0
  • webpack hot-module-replacement 原理&amp;踩坑

    摘要:原理踩坑起因最近在做框架的热更新,记录一下的原理和小坑。文件系统接收更改并通知。运行时通过请求这些更新。类似的问题还有很多,事件绑定手动插入并且没有销毁的定时器等,记得把这些副作用一起干掉。参考官方文档原理分析与实现 webpack hot-module-replacement 原理&踩坑 起因 最近在做san框架的热更新,记录一下webpack HMR的原理和小坑。 什么是HMR? ...

    elva 评论0 收藏0
  • (译 &amp; 转载) 2016 JavaScript 后起之秀

    摘要:在年成为最大赢家,赢得了实现的风暴之战。和他的竞争者位列第二没有前端开发者可以忽视和它的生态系统。他的杀手级特性是探测功能,通过检查任何用户的功能,以直观的方式让开发人员检查所有端点。 2016 JavaScript 后起之秀 本文转载自:众成翻译译者:zxhycxq链接:http://www.zcfy.cc/article/2410原文:https://risingstars2016...

    darry 评论0 收藏0
  • webpack多页应用架构系列(十二):利用webpack生成HTML普通网页&amp;页面模板

    摘要:彻底分离源文件目录和生成文件目录使用生成出来的页面可以很安心地跟打包好的其它资源放到一起,相对于另起一个目录专门存放页面文件来说,整个文件目录结构更加合理如何利用生成页面生成页面主要是通过来实现的,下面来介绍如何实现。 本文首发于Array_Huang的技术博客——实用至上,非经作者同意,请勿转载。原文地址:https://segmentfault.com/a/119000000712...

    BaronZhang 评论0 收藏0
  • webpack&amp;&amp;loader

    摘要:本质上是一个可以模块化使用的加载处理的函数。本文着重讨论以作为样本分析。下面一幅图分别是单和双编译结果图。总结通过简单的例子复习了编译后的文件执行流程探索了编译流程研究意义还是有的。相关问题参考链接参考链接 引言 在回答一个问题时,引发一些疑问,分析总结下,作为备忘 webpack webpack对于我来说,应用场景主要是,编译打包我通过模块化组织书写的文件,用其提供的各种loader...

    yuanzhanghu 评论0 收藏0

发表评论

0条评论

roland_reed

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<