资讯专栏INFORMATION COLUMN

浅析webpack源码之Compilation.js粗解(九)

Tangpj / 1281人阅读

摘要:编写良好的模块提供了可靠的抽象和封装边界,构成了一致的设计和明确的目的。块此特定术语在内部用于管理捆绑过程。捆绑包由块组成,其中有几种类型例如入口和子。总结一个块是进程中的一组模块,一个是一个发出的块或一组块。

我们先看一下 compilation是什么?
是一个很大的对象
打印key值

[ "_pluginCompat",
  "hooks",
  "name",
  "compiler",
  "resolverFactory",
  "inputFileSystem",
  "requestShortener",
  "options",
  "outputOptions",
  "bail",
  "profile",
  "performance",
  "mainTemplate",
  "chunkTemplate",
  "hotUpdateChunkTemplate",
  "runtimeTemplate",
  "moduleTemplates",
  "semaphore",
  "entries",
  "_preparedEntrypoints",
  "entrypoints",
  "chunks",
  "chunkGroups",
  "namedChunkGroups",
  "namedChunks",
  "modules",
  "_modules",
  "cache",
  "records",
  "additionalChunkAssets",
  "assets",
  "errors",
  "warnings",
  "children",
  "dependencyFactories",
  "dependencyTemplates",
  "childrenCounters",
  "usedChunkIds",
  "usedModuleIds",
  "fileTimestamps",
  "contextTimestamps",
  "compilationDependencies",
  "_buildingModules",
  "_rebuildingModules"

我们看有熟悉的chunks,entries,options,
这些是Compilation定义的属性

const EntryModuleNotFoundError = require("./EntryModuleNotFoundError");
const ModuleNotFoundError = require("./ModuleNotFoundError");
const ModuleDependencyWarning = require("./ModuleDependencyWarning");
const ModuleDependencyError = require("./ModuleDependencyError");
const ChunkGroup = require("./ChunkGroup");
const Chunk = require("./Chunk");
const Entrypoint = require("./Entrypoint");
const MainTemplate = require("./MainTemplate");
const ChunkTemplate = require("./ChunkTemplate");
const HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate");
const ModuleTemplate = require("./ModuleTemplate");
const RuntimeTemplate = require("./RuntimeTemplate");
const ChunkRenderError = require("./ChunkRenderError");
const AsyncDependencyToInitialChunkError = require("./AsyncDependencyToInitialChunkError");
const Stats = require("./Stats");
const Semaphore = require("./util/Semaphore");
const createHash = require("./util/createHash");
const Queue = require("./util/Queue");
const SortableSet = require("./util/SortableSet");
const GraphHelpers = require("./GraphHelpers");
const ModuleDependency = require("./dependencies/ModuleDependency");
const compareLocations = require("./compareLocations");

从这一堆的模块引入里,也可以猜出Compilation主要做了Modules变成Chunks编译的过程

里面有一些方法

1.对模块的处理,创建搜索添加构建/模块依赖处理,添加模块依赖,分类模块等
addModule/getModule/ findModule/buildModule/sortModules
2.对Chunk的处理,创建Chunk
3.哈希值

Compilation.prototype.applyPlugins

在原型上添加了applyPlugins方法

Compilation.js也是对Tapable 的拓展
主要处理Chunk、Module等

我们学习一下

webpack术语表定义

模块:离散的功能块,提供比完整程序更小的表面积。编写良好的模块提供了可靠的抽象和封装边界,构成了一致的设计和明确的目的。

块:此Webpack特定术语在内部用于管理捆绑过程。捆绑包由块组成,其中有几种类型(例如入口和子)。通常,块直接与输出束相对应,但是,有些配置不会产生一对一的关系。

Bundle:由许多不同的模块生成,bundle包含已经过加载和编译过程的源文件的最终版本。

总结
a chunk is a group of modules within the webpack process, a bundle is an emitted chunk or set of chunks.

一个块是webpack进程中的一组模块,一个bundle是一个发出的块或一组块。

{
  entry: {
    foo: ["webpack/hot/only-dev-server.js","./src/foo.js"],
    bar: ["./src/bar.js"]
  },
  output: {
    path: "./dist",
    filename: "[name].js"
  }
}
Modules: "webpack/hot/only-dev-server.js", "./src/foo.js", "./src/bar.js" ( + any other modules that are dependencies of these entry points!)
Chunks: foo, bar
Bundles: foo, bar

通俗的解释一下,Modules是引入的模块,Chunks就是编译的模块,Bundles是提交的Chunks ,Chunks和Bundles是1:1的关系,配置map会有例外

总结

Compilation主要做了Modules变成Chunks编译的过程

chunks

不细看逻辑,好像太虚了,我们看一下this.chunks

它是一个数组,目前Chunk的长度是1,因为只是引入了一个模块

[ Chunk {
    id: "main",
    ids: [ "main" ],
    debugId: 1000,
    name: "main",
    preventIntegration: false,
    entryModule:
     NormalModule {
       dependencies: [],
       blocks: [],
       variables: [],
       type: "javascript/auto",
       context: "/Users/orion/Desktop/react-beauty-highcharts/src",
       debugId: 1000,
       hash: "a6388d29fa15bd58c6cffb10246992a5",
       renderedHash: "a6388d29fa15bd58c6cf",
       resolveOptions: {},
       factoryMeta: {},
       warnings: [],
       errors: [],
       buildMeta: [Object],
       buildInfo: [Object],
       reasons: [Array],
       _chunks: [SortableSet],
       id: "./src/index.js",
       index: 0,
       index2: 0,
       depth: 0,
       issuer: null,
       profile: undefined,
       prefetched: false,
       built: true,
       used: null,
       usedExports: null,
       optimizationBailout: [],
       _rewriteChunkInReasons: undefined,
       useSourceMap: true,
       _source: [SourceMapSource],
       request:
        "/Users/orion/Desktop/react-beauty-highcharts/node_modules/babel-loader/lib/index.js!/Users/orion/Desktop/react-beauty-highcharts/src/index.js",
       userRequest: "/Users/orion/Desktop/react-beauty-highcharts/src/index.js",
       rawRequest: "./src/index.js",
       binary: false,
       parser: [Parser],
       generator: JavascriptGenerator {},
       resource: "/Users/orion/Desktop/react-beauty-highcharts/src/index.js",
       matchResource: undefined,
       loaders: [Array],
       error: null,
       _buildHash: "488efbd43aa05371d3f44d94c89abd57",
       buildTimestamp: 1547884969828,
       _cachedSources: Map {},
       lineToLine: false,
       _lastSuccessfulBuildMeta: [Object],
       _ast: null },
    _modules:
     SortableSet [Set] {
       [NormalModule],
       _sortFn: [Function: sortByIdentifier],
       _lastActiveSortFn: null,
       _cache: undefined,
       _cacheOrderIndependent: undefined },
    filenameTemplate: undefined,
    _groups:
     SortableSet [Set] {
       [Entrypoint],
       _sortFn: [Function: sortChunkGroupById],
       _lastActiveSortFn: null,
       _cache: undefined,
       _cacheOrderIndependent: undefined },
    files: [],
    rendered: false,
    hash: "0988e8454f1915ec05fee482db8d0a6f",
    contentHash: { javascript: "4b8695ca3c1d42e76c52" },
    renderedHash: "0988e8454f1915ec05fe",
    chunkReason: undefined,
    extraAsync: false,
    removedModules: undefined } ]

我们看到在entryModule下,记录了入口的绝对地址,相对地址,编译的hash,文件类型等信息

//文件写入,文件输出,文件缓存,里面具体的template.getRenderManifest,chunk.hasRuntime(),CachedSource具体的逻辑不能够一一的去研究详解,但是从名字能知道这个函数是做什么的
createChunkAssets() {
        const outputOptions = this.outputOptions;
        const cachedSourceMap = new Map();
        /** @type {Map} */
        const alreadyWrittenFiles = new Map();
        for (let i = 0; i < this.chunks.length; i++) {
            const chunk = this.chunks[i];
            chunk.files = [];
            let source;
            let file;
            let filenameTemplate;
            try {
                const template = chunk.hasRuntime()
                    ? this.mainTemplate
                    : this.chunkTemplate;
                const manifest = template.getRenderManifest({
                    chunk,
                    hash: this.hash,
                    fullHash: this.fullHash,
                    outputOptions,
                    moduleTemplates: this.moduleTemplates,
                    dependencyTemplates: this.dependencyTemplates
                }); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
                for (const fileManifest of manifest) {
                    const cacheName = fileManifest.identifier;
                    const usedHash = fileManifest.hash;
                    filenameTemplate = fileManifest.filenameTemplate;
                    file = this.getPath(filenameTemplate, fileManifest.pathOptions);

                    // check if the same filename was already written by another chunk
                    const alreadyWritten = alreadyWrittenFiles.get(file);
                    if (alreadyWritten !== undefined) {
                        if (alreadyWritten.hash === usedHash) {
                            if (this.cache) {
                                this.cache[cacheName] = {
                                    hash: usedHash,
                                    source: alreadyWritten.source
                                };
                            }
                            chunk.files.push(file);
                            this.hooks.chunkAsset.call(chunk, file);
                            continue;
                        } else {
                            throw new Error(
                                `Conflict: Multiple chunks emit assets to the same filename ${file}` +
                                    ` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})`
                            );
                        }
                    }
                    if (
                        this.cache &&
                        this.cache[cacheName] &&
                        this.cache[cacheName].hash === usedHash
                    ) {
                        source = this.cache[cacheName].source;
                    } else {
                        source = fileManifest.render();
                        // Ensure that source is a cached source to avoid additional cost because of repeated access
                        if (!(source instanceof CachedSource)) {
                            const cacheEntry = cachedSourceMap.get(source);
                            if (cacheEntry) {
                                source = cacheEntry;
                            } else {
                                const cachedSource = new CachedSource(source);
                                cachedSourceMap.set(source, cachedSource);
                                source = cachedSource;
                            }
                        }
                        if (this.cache) {
                            this.cache[cacheName] = {
                                hash: usedHash,
                                source
                            };
                        }
                    }
                    if (this.assets[file] && this.assets[file] !== source) {
                        throw new Error(
                            `Conflict: Multiple assets emit to the same filename ${file}`
                        );
                    }
                    this.assets[file] = source;
                    chunk.files.push(file);
                    this.hooks.chunkAsset.call(chunk, file);
                    alreadyWrittenFiles.set(file, {
                        hash: usedHash,
                        source,
                        chunk
                    });
                }
            } catch (err) {
                this.errors.push(
                    new ChunkRenderError(chunk, file || filenameTemplate, err)
                );
            }
        }
    }
modules
[ {
    dependencies: [],
    blocks: [],
    variables: [],
    type: "javascript/auto",
    context: "/Users/orion/Desktop/react-beauty-highcharts/src",
    debugId: 1000,
    hash: "a6388d29fa15bd58c6cffb10246992a5",
    renderedHash: "a6388d29fa15bd58c6cf",
    resolveOptions: {},
    factoryMeta: {},
    warnings: [],
    errors: [],
    buildMeta: { providedExports: true },
    buildInfo:
     { cacheable: true,
       fileDependencies: [Set],
       contextDependencies: Set {},
       temporaryProvidedExports: false },
    reasons: [ [ModuleReason] ],
    _chunks:{
       [Chunk],
       _sortFn: [Function: sortById],
       _lastActiveSortFn: null,
       _cache: undefined,
       _cacheOrderIndependent: undefined },
    id: "./src/index.js",
    index: 0,
    index2: 0,
    depth: 0,
    issuer: null,
    profile: undefined,
    prefetched: false,
    built: true,
    used: null,
    usedExports: null,
    optimizationBailout: [],
    _rewriteChunkInReasons: undefined,
    useSourceMap: true,
    _source:{
       _value:
        "module.exports = {
  doubleLine: function doubleLine(arr) {
    if (arr && !Array.isArray(arr)) {
      console.error("the first params type must be Array");
      return;
    }

    return {
      credits: {
        enabled: false // 禁用版权信息

      },
      chart: {
        width: "400",
        height: "400",
        type: "area",
        backgroundColor: {
          linearGradient: [0, 0, 500, 500],
          stops: [[0, "rgba(14, 8, 55,1)"], [1, "rgba(14, 8, 55,1)"]]
        }
      },
      title: {
        text: "",
        style: {
          color: "#a6aed2",
          font: "bold 16px "Trebuchet MS", Verdana, sans-serif"
        }
      },
      xAxis: {
        categories: ["10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", "15:00", "15:30", "16:00", "16:30", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00"],
        labels: {
          format: "{value} ",
          style: {
            color: "#746f95",
            fontSize: "12px",
            fontFamily: "微软雅黑"
          }
        },
        maxPadding: 0.05,
        showLastLabel: true,
        // tickmarkPlacement:"on",
        tickColor: "#746f95",
        lineWidth: 1,
        lineColor: "#746f95",
        tickLength: 5,
        minRange: 5,
        tickInterval: 1 // 坐标轴刻度间隔为一星期

      },
      yAxis: {
        gridLineWidth: "0px",
        startOnTick: true,
        endOnTick: false,
        maxPadding: 0.35,
        title: {
          text: null
        },
        labels: {
          // format: "{value} m",
          style: {
            color: "#746f95",
            fontSize: "12px",
            fontFamily: "微软雅黑"
          }
        },
        lineWidth: 1,
        lineColor: "#746f95"
      },
      legend: {
        itemStyle: {
          font: "9pt Trebuchet MS, Verdana, sans-serif",
          color: "#a6aed2"
        },
        itemHoverStyle: {
          color: "#fff"
        }
      },
      tooltip: {
        pointFormat: "{series.name}:  {point.y:,.0f}人"
      },
      plotOptions: {
        area: {
          // pointStart: 1920,
          marker: {
            enabled: false,
            symbol: "circle",
            radius: 2,
            states: {
              hover: {
                enabled: true
              }
            }
          }
        }
      },
      series: arr && arr[0] === "pink" ? [{
        // data: [  110, 235, 369, 640,
        //        1005, 1436, 2063, 3057, 4618, 6444, 9822, 15468, 20434, 24126,
        //        27387, 29459, 31056, 31982, 32040, 31233, 29224, 27342, 26662,
        //        26956, 27912, 28999, 28965, 27826, 25579, 25722, 24826, 24605,
        //        24304, 23464, 23708, 24099, 24357, 24237, 24401, 24344, 23586,
        //        22380, 21004, 17287, 14747, 13076, 12555, 12144, 11009, 10950,
        //        10871, 10824, 10577, 10527, 10475, 10421, 10358, 10295, 10104],
        lineColor: "#e88eb3",
        color: {
          linearGradient: {
            x1: 0,
            x2: 0,
            y1: 0,
            y2: 1
          },
          stops: [[0, "rgba(231,142,179,0.8)"], [1, "rgba(135,56,89,0.5)"]]
        },
        fillOpacity: 0.5,
        name: "进",
        marker: {
          enabled: false
        } // threshold: null // 是否显示负数

      }, {
        // data: ,
        lineColor: "#b946ff",
        color: {
          linearGradient: {
            x1: 0,
            x2: 0,
            y1: 0,
            y2: 1
          },
          stops: [[0, "rgba(152,60,210,0.8)"], [1, "rgba(65,25,90,0.35)"]]
        },
        fillOpacity: 0.5,
        name: "出",
        marker: {
          enabled: false
        },
        threshold: null
      }] : [{
        // data: [  110, 235, 369, 640,
        //        1005, 1436, 2063, 3057, 4618, 6444, 9822, 15468, 20434, 24126,
        //        27387, 29459, 31056, 31982, 32040, 31233, 29224, 27342, 26662,
        //        26956, 27912, 28999, 28965, 27826, 25579, 25722, 24826, 24605,
        //        24304, 23464, 23708, 24099, 24357, 24237, 24401, 24344, 23586,
        //        22380, 21004, 17287, 14747, 13076, 12555, 12144, 11009, 10950,
        //        10871, 10824, 10577, 10527, 10475, 10421, 10358, 10295, 10104],
        lineColor: "#b946ff",
        color: {
          linearGradient: {
            x1: 0,
            x2: 0,
            y1: 0,
            y2: 1
          },
          stops: [[0, "rgba(152,60,210,0.8)"], [1, "rgba(65,25,90,0.35)"]]
        },
        fillOpacity: 0.5,
        name: "进",
        marker: {
          enabled: false
        } // threshold: null // 是否显示负数

      }, {
        // data: ,
        lineColor: "#68d5ee",
        color: {
          linearGradient: {
            x1: 0,
            x2: 0,
            y1: 0,
            y2: 1
          },
          stops: [[0, "rgba(104,213,238,0.8)"], [1, "rgba(7,44,96,0.5)"]]
        },
        fillOpacity: 0.5,
        name: "出",
        marker: {
          enabled: false
        },
        threshold: null
      }]
    };
  },
  columns: function columns() {
    return {
      credits: {
        enabled: false
      },
      chart: {
        type: "column",
        backgroundColor: {
          linearGradient: [0, 0, 500, 500],
          stops: [[0, "rgb(14, 8, 55)"], [1, "rgb(14, 8, 55)"]]
        }
      },
      title: {
        text: "月平均降雨量",
        style: {
          color: "#a6aed2",
          font: "bold 16px "Trebuchet MS", Verdana, sans-serif"
        }
      },
      xAxis: {
        gridLineWidth: "0px",
        startOnTick: true,
        endOnTick: false,
        maxPadding: 0.35,
        categories: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
        labels: {
          format: "{value} ",
          style: {
            color: "#746f95",
            fontSize: "12px",
            fontFamily: "微软雅黑"
          }
        },
        crosshair: true,
        lineWidth: 1,
        lineColor: "#746f95",
        tickLength: 5,
        tickColor: "#746f95"
      },
      yAxis: {
        gridLineWidth: "0px",
        startOnTick: true,
        endOnTick: false,
        maxPadding: 0.35,
        min: 0,
        labels: {
          format: "{value} ",
          style: {
            color: "#746f95",
            fontSize: "12px",
            fontFamily: "微软雅黑"
          }
        },
        title: {
          // 平均时长 (min)
          text: "",
          style: {
            color: "#746f95",
            fontSize: "12px",
            fontFamily: "微软雅黑"
          }
        },
        lineWidth: 1,
        lineColor: "#746f95"
      },
      tooltip: {
        // head + 每个 point + footer 拼接成完整的 table
        headerFormat: "{point.key}",
        pointFormat: "" + "",
        footerFormat: "
{series.name}: {point.y}
", shared: true, useHTML: true }, plotOptions: { column: { borderWidth: 0 } }, legend: { itemStyle: { font: "9pt Trebuchet MS, Verdana, sans-serif", color: "#a6aed2" }, itemHoverStyle: { color: "#fff" } }, series: [{ color: "#3453d4", name: ">120s", data: [1, 2, 3, 4, 5, 6, 7] }, { color: "#ff2674", name: "60~120s", data: [3, 4, 3, 1, 3, 2, 2] }, { color: "#66c3e3", // color:"#66c3e3", name: "<60s", data: [7, 4, 3, 4, 2, 6, 8] }] }; } };", _name: "/Users/orion/Desktop/react-beauty-highcharts/node_modules/babel-loader/lib/index.js!/Users/orion/Desktop/react-beauty-highcharts/src/index.js", _sourceMap: [Object], _originalSource: undefined, _innerSourceMap: undefined }, request: "/Users/orion/Desktop/react-beauty-highcharts/node_modules/babel-loader/lib/index.js!/Users/orion/Desktop/react-beauty-highcharts/src/index.js", userRequest: "/Users/orion/Desktop/react-beauty-highcharts/src/index.js", rawRequest: "./src/index.js", binary: false, parser:{ _pluginCompat: [SyncBailHook], hooks: [Object], options: {}, sourceType: "auto", scope: undefined, state: undefined, comments: undefined }, generator: JavascriptGenerator {}, resource: "/Users/orion/Desktop/react-beauty-highcharts/src/index.js", matchResource: undefined, loaders: [ [Object] ], error: null, _buildHash: "488efbd43aa05371d3f44d94c89abd57", buildTimestamp: 1547885885262, _cachedSources: Map {}, lineToLine: false, _lastSuccessfulBuildMeta: { providedExports: true }, _ast: null } ]

我们看到module的长度也是一个,一样有类型,路径,hash
其中
_source的_value下的module.exports,已经是略微压缩版的了,里面还有n

里面值得分析的还有很多,关于怎么压缩,压缩算法是怎么处理的,二次读源码再详解

周六快乐,刚刚抓娃娃机一个都没抓上,哈哈哈哈,但是我抓的激动开心啊

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

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

相关文章

  • 浅析webpack源码Tapable粗解(五)

    摘要:打开是个构造函数,定义了一些静态属性和方法我们先看在插件下地址上面写的解释就跟没写一样在文件下我们看到输出的一些对象方法每一个对应一个模块而在下引入的下面,我们先研究引入的对象的英文单词解释,除了最常用的点击手势之外,还有一个意思是水龙头进 打开compile class Compiler extends Tapable { constructor(context) { ...

    Arno 评论0 收藏0
  • 浅析webpack源码Stat.js粗解(十)

    摘要:从出来接着我们看大法,打印一下感觉之前所以的对象都放在了一个合集里,给人而全的感觉里面主要含有一个对象,,输出的,等给每次打包一个值,代表唯一性天啊 从compilation出来接着我们看 const stats = new Stats(compilation); Stats.js log大法,打印一下 stats let Stats = { compilation:{ ...

    Andrman 评论0 收藏0
  • 关于 webpack 你可能忽略的细节(附源码分析)

    摘要:本篇的主要目标是通过实际问题来介绍中容易被人忽略的细节以及源码分析以最新发布的版本的源码为例并且提供几种解决方案。探究原因及源码分析这里以最新发布的版本的源码作为分析。解决方案加参数基于上面简要的分析,我们来尝试下参数的作用。 注:本篇不是入门教程,入门请直接查看官方文档。本篇的主要目标是通过实际问题来介绍 webpack 中容易被人忽略的细节, 以及源码分析(以最新发布的 relea...

    mtunique 评论0 收藏0
  • 你用 webpack 1.x 输出的 hash 靠谱不?

    摘要:举例这个方案还有些小缺点,就是用模块文件路径作为哈希输入还不是百分百完美,如果文件名改了,那么模块就不稳定了。其实,可以用模块文件内容作为哈希输入,考虑到效率问题,权衡之下还是用路径好了。 来自 http://zhenyong.site/2016/10/... 使用 webpack 构建输出文件时,通常会给文件名加上 hash,该 hash 值根据文件内容计算得到,只要文件内容不变,h...

    sunny5541 评论0 收藏0
  • 浅析webpack源码前言(一)

    为什么读webpack源码 因为前端框架离不开webpack,天天都在用的东西啊,怎能不研究 读源码能学到很多做项目看书学不到的东西,比如说架构,构造函数,es6很边缘的用法,甚至给函数命名也会潜移默化的影响等 想写源码,不看源码怎么行,虽然现在还不知道写什么,就算不写什么,看看别人写的总可以吧 知道世界的广阔,那么多插件,那么多软件开发师,他们在做什么,同样是写js的,怎么他们能这么伟大 好奇...

    suosuopuo 评论0 收藏0

发表评论

0条评论

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