1. 程式人生 > >有了它們就能更好的理解webpack了

有了它們就能更好的理解webpack了

最新內容,請在github閱讀。同時,All issue and star welcomed!

1.獲取webpack配置的輸出配置

 compilation.outputOptions

其中該物件有如下內容:

 { path: 'builds',
     filename: 'bundle.js',
     publicPath: 'builds/',
     chunkFilename: '[id].bundle.js',
     library: '',
     hotUpdateFunction: 'webpackHotUpdate',
     jsonpFunction: 'webpackJsonp'
, libraryTarget: 'var', sourceMapFilename: '[file].map[query]', hotUpdateChunkFilename: '[id].[hash].hot-update.js', hotUpdateMainFilename: '[hash].hot-update.json', crossOriginLoading: false, hashFunction: 'md5', hashDigest: 'hex', hashDigestLength: 20, devtoolLineToLine: false
, strictModuleExceptionHandling: false }

如果你需要更多的資訊只能通過如下:

compilation.options
  {
   entry: './src',
  output:
   { path: 'builds',
     filename: 'bundle.js',
     publicPath: 'builds/',
     chunkFilename: '[id].bundle.js',
     library: '',
     hotUpdateFunction: 'webpackHotUpdate',
     jsonpFunction: 'webpackJsonp',
     libraryTarget: 'var',
     sourceMapFilename: '[file].map[query]',
     hotUpdateChunkFilename: '[id].[hash].hot-update.js',
     hotUpdateMainFilename: '[hash].hot-update.json',
     crossOriginLoading: false,
     hashFunction: 'md5',
     hashDigest: 'hex',
     hashDigestLength: 20,
     devtoolLineToLine: false,
     strictModuleExceptionHandling: false },
  plugins:
   [ CommonsChunkPlugin {
       chunkNames: 'vendor',
       filenameTemplate: 'vendor.bundle.js',
       minChunks: 2,
       selectedChunks: undefined,
       async: undefined,
       minSize: undefined,
       ident: 'C:\\Users\\Administrator\\Desktop\\webpack-chunkfilename\\node_mo
dules\\webpack\\lib\\optimize\\CommonsChunkPlugin.js0' },
     HtmlWebpackPlugin {
       options: [Object],
       childCompilerHash: '729d3caf962246f308dc6d5b1542a9ae',
       childCompilationOutputName: 'index.html' } ],
  module:
   { loaders: [ [Object], [Object], [Object], [Object] ],
     unknownContextRequest: '.',
     unknownContextRegExp: false,
     unknownContextRecursive: true,
     unknownContextCritical: true,
     exprContextRequest: '.',
     exprContextRegExp: false,
     exprContextRecursive: true,
     exprContextCritical: true,
     wrappedContextRegExp: /.*/,
     wrappedContextRecursive: true,
     wrappedContextCritical: false,
     unsafeCache: true },
  bail: false,
  profile: false,
  context: 'C:\\Users\\Administrator\\Desktop\\webpack-chunkfilename',
  devtool: false,
  cache: true,
  target: 'web',
  
  node:
   { console: false,
     process: true,
     global: true,
     Buffer: true,
     setImmediate: true,
     __filename: 'mock',
     __dirname: 'mock' },
  performance: { maxAssetSize: 250000, maxEntrypointSize: 250000, hints: false }
,
  resolve:
   { unsafeCache: true,
     modules: [ 'node_modules' ],
     extensions: [ '.js', '.json' ],
     aliasFields: [ 'browser' ],
     mainFields: [ 'browser', 'module', 'main' ] },
  resolveLoader:
   { unsafeCache: true,
     mainFields: [ 'loader', 'main' ],
     extensions: [ '.js', '.json' ] } }

注意我們其中關注的幾點,node模組!

下面給出一個獲取publicPath(輸出配置)路徑的例子:

 var webpackStatsJson = compilation.getStats().toJson();
  //獲取compilation的所有的資訊
  // Use the configured public path or build a relative path
  var publicPath = typeof compilation.options.output.publicPath !== 'undefined'
    // If a hard coded public path exists use it
    ? compilation.mainTemplate.getPublicPath({hash: webpackStatsJson.hash})
    // If no public path was set get a relative url path
    // 這個publicPath是可以使用hash的
    : path.relative(path.resolve(compilation.options.output.path, path.dirname(self.childCompilationOutputName)), compilation.options.output.path)
      .split(path.sep).join('/');
    //self.childCompilationOutputName得到的"index.html",dirname得到的是".",所以resolve結果為" C:\Users\Administrator\Desktop\webpack-chunkfilename\builds"
    if (publicPath.length && publicPath.substr(-1, 1) !== '/') {
      publicPath += '/';
    }
    //獲取倒數第一個字元,新增一個"/"

很顯然我們的publicPath是可以配置hash引數的,如果沒有配置publicPath那麼我們是相對於output.path目錄來說的!

2.如何獲取啟動一個子compiler中的錯誤資訊

if (childCompilation && childCompilation.errors && childCompilation.errors.length) {
        var errorDetails = childCompilation.errors.map(function (error) {
          return error.message + (error.error ? ':\n' + error.error : '');
        }).join('\n');
        reject(new Error('Child compilation failed:\n' + errorDetails));
        //如果報錯,直接reject
      }

其實對於compiler物件,而不是childCompiler物件也是同樣的道理

3.開啟的child compiler如何對檔案中的hash等進行替換得到最終檔名

   //'assets-path'鉤子函式來完成檔名替
   var outputName = compilation.mainTemplate.applyPluginsWaterfall('asset-path', outputOptions.filename, {
          hash: childCompilation.hash,
          chunk: entries[0]
          //因為上面提供的是SingleEntryPlugin ·
        });

注意:此處呼叫的是compilation而不是childCompilation完成的!

4.如何載入本地檔案系統的檔案然後開啟一個新的child c compiler

HtmlWebpackPlugin.prototype.getFullTemplatePath = function (template, context) {
  // If the template doesn't use a loader use the lodash template loader
  if (template.indexOf('!') === -1) {
    template = require.resolve('./lib/loader.js') + '!' + path.resolve(context, template);
    //對於我們的這個ejs檔案,我們使用特定的loader載入
  }
  // Resolve template path
  return template.replace(
    /([!])([^/\\][^!?]+|[^/\\!?])($|\?[^!?\n]+$)/,
    function (match, prefix, filepath, postfix) {
      return prefix + path.resolve(filepath) + postfix;
    });
};
//通過上面的getFullTemplatePath得到通過loader載入檔案後的完整的形式,最後傳入到我們的SingleEntryPlugin中
  childCompiler.apply(
    new NodeTemplatePlugin(outputOptions),
    new NodeTargetPlugin(),
    new LibraryTemplatePlugin('HTML_WEBPACK_PLUGIN_RESULT', 'var'),
    new SingleEntryPlugin(this.context, template),
    //上面的通過loader載入的本地檔案系統的內容傳入到我們的SingleEntryPlugin中
    new LoaderTargetPlugin('node')
  );

所以上面的傳入到中的內容大致如下(其中!後面表示要載入的檔案,前面表示相應的loader):

C:\Users\Administrator\Desktop\webpack-chunkfilename\node_modules\html-webpack-plugin\lib\loader.js!C:\Users\Administrator\Desktop\w
ebpack-chunkfilename\node_modules\html-webpack-plugin\default_index.ejs

4.獲取stats中所有的chunks

獲取compilation所有的chunks:

compilation.getStats().toJson().chunks

chunks所有的內容如下:

[ { id: 0,//chunk id
    rendered: true,
    initial: false,//require.ensure產生,非initial
    entry: false,//非入口檔案
    recorded: undefined,
    extraAsync: false,
    size: 296855,//chunk大小,位元
    names: [],//require.ensure不是通過webpack配置的,所以chunk的names是空
    files: [ '0.bundle.js' ],//該chunk產生的檔案
    hash: '42fbfbea594ba593e76a',//chunk的hash
    parents: [ 2 ],//父級chunk
    origins: [ [Object] ] },
  { id: 1,
    rendered: true,
    initial: false,//require.ensure產生,非initial
    entry: false,//非入口檔案
    recorded: undefined,
    extraAsync: false,
    size: 297181,//chunk大小,位元
    names: [],
    files: [ '1.bundle.js' ],//產生的檔案
    hash: '456d05301e4adca16986',//chunk的hash
    parents: [ 2 ],
    origins: [ [Object] ] },
  { id: 2,
    rendered: true,
    initial: true,//commonchunkplugin產生或者入口檔案產生
    entry: false,//非入口檔案
    recorded: undefined,
    extraAsync: false,
    size: 687,//chunk大小,位元
    names: [ 'main' ],
    files: [ 'bundle.js' ],//產生的檔案
    hash: '248029a0cfd99f46babc',//chunk的hash
    parents: [ 3 ],
    origins: [ [Object] ] },
  { id: 3,
    rendered: true,
    initial: true,//monchunkplugin產生或者入口檔案產生
    entry: true,//commonchunkplugin把webpack執行環境抽取出來
    recorded: undefined,
    extraAsync: false,
    size: 0,//chunk大小,位元
    names: [ 'vendor' ],
    files: [ 'vendor.bundle.js' ],//產生的檔案
    hash: 'fbf76c7c330eaf0de943',//chunk的hash
    parents: [],
    origins: [] } ]

注意chunk的names是一個數組!

下面是給出的幾個關於chunks的例子:

例1:html-webpack-plugin中就使用到了多個chunks屬性,如names,initial等

//該chunk要被選中的條件是:有名稱,不是懶載入,在includedChunks中但是不在excludedChunks中
HtmlWebpackPlugin.prototype.filterChunks = function (chunks, includedChunks, excludedChunks) {
  return chunks.filter(function (chunk) {
    var chunkName = chunk.names[0];
    // This chunk doesn't have a name. This script can't handled it.
    //通過require.ensure產生的chunk不會被保留,names是一個數組
    if (chunkName === undefined) {
      return false;
    }
    // Skip if the chunk should be lazy loaded
    //如果是require.ensure產生的chunk直接忽略
    if (!chunk.initial) {
      return false;
    }
    // Skip if the chunks should be filtered and the given chunk was not added explicity
    //這個chunk必須在includedchunks裡面
    if (Array.isArray(includedChunks) && includedChunks.indexOf(chunkName) === -1) {
      return false;
    }
    // Skip if the chunks should be filtered and the given chunk was excluded explicity
    //這個chunk不能在excludedChunks中
    if (Array.isArray(excludedChunks) && excludedChunks.indexOf(chunkName) !== -1) {
      return false;
    }
    // Add otherwise
    return true;
  });
};

例2:通過id對chunks進行排序

/**
 * Sorts the chunks based on the chunk id.
 *
 * @param  {Array} chunks the list of chunks to sort
 * @return {Array} The sorted list of chunks
 * entry chunk在前,兩個都是entry那麼id大的在前
 */
module.exports.id = function (chunks) {
  return chunks.sort(function orderEntryLast (a, b) {
    if (a.entry !== b.entry) {
      return b.entry ? 1 : -1;
    } else {
      return b.id - a.id;
    }
  });
};

這樣入口檔案都會排列在前面,但是有一點就是這裡的id較大的在前面,這和我們的occurenceOrderplugin完全是相反的,所以這裡還是存在疑問的。

例3:通過chunk.parents(全部是parentId陣列)來獲取拓排序

/*
  Sorts dependencies between chunks by their "parents" attribute.
  This function sorts chunks based on their dependencies with each other.
  The parent relation between chunks as generated by Webpack for each chunk
  is used to define a directed (and hopefully acyclic) graph, which is then
  topologically sorted in order to retrieve the correct order in which
  chunks need to be embedded into HTML. A directed edge in this graph is
  describing a "is parent of" relationship from a chunk to another (distinct)
  chunk. Thus topological sorting orders chunks from bottom-layer chunks to
  highest level chunks that use the lower-level chunks.

  @param {Array} chunks an array of chunks as generated by the html-webpack-plugin.
  It is assumed that each entry contains at least the properties "id"
  (containing the chunk id) and "parents" (array containing the ids of the
  parent chunks).
  @return {Array} A topologically sorted version of the input chunks
  因為最上面的通過commonchunkplugin產生的chunk具有webpack的runtime,所以排列在前面
*/
module.exports.dependency = function (chunks) {
  if (!chunks) {
    return chunks;
  }
  // We build a map (chunk-id -> chunk) for faster access during graph building.
  // 通過chunk-id -> chunk這種Map結構更加容易繪製圖
  var nodeMap = {};
  chunks.forEach(function (chunk) {
    nodeMap[chunk.id] = chunk;
  });
  // Next, we add an edge for each parent relationship into the graph
  var edges = [];
  chunks.forEach(function (chunk) {
    if (chunk.parents) {
      // Add an edge for each parent (parent -> child)
      chunk.parents.forEach(function (parentId) {
        // webpack2 chunk.parents are chunks instead of string id(s)
        var parentChunk = _.isObject(parentId) ? parentId : nodeMap[parentId];
        // If the parent chunk does not exist (e.g. because of an excluded chunk)
        // we ignore that parent
        if (parentChunk) {
          edges.push([parentChunk, chunk]);
        }
      });
    }
  });
  // We now perform a topological sorting on the input chunks and built edges
  return toposort.array(chunks, edges);
};

通過這種方式可以把各個chunk公有的模組排列在前面,從而提前載入,這是合理的!

5.獲取stats中所有的assets

獲取compilation所有的assets:

compilation.getStats().toJson().assets

assets內部結構如下:

 [ { name: '0.bundle.js',
    size: 299109,
    chunks: [ 0, 3 ],
    
    chunkNames: [],
    emitted: undefined,
    isOverSizeLimit: undefined },
  { name: '1.bundle.js',
    size: 299469,
    chunks: [ 1, 3 ],
    chunkNames: [],
    emitted: undefined,
    isOverSizeLimit: undefined },
  { name: 'bundle.js',
    
    size: 968,
    
    chunks: [ 2, 3 ],
    
    chunkNames: [ 'main' ],
    
    emitted: undefined,
    
    isOverSizeLimit: undefined },
  { name: 'vendor.bundle.js',
    size: 5562,
    chunks: [ 3 ],
    chunkNames: [ 'vendor' ],
    emitted: undefined,
    isOverSizeLimit: undefined }]

上次看到webpack-dev-server原始碼的時候看到了如何判斷該assets是否變化的判斷,其就是通過上面的emitted來判斷的:

compiler.plugin("done", function(stats) {
    this._sendStats(this.sockets, stats.toJson(clientStats));
    //clientStats表示客戶端stats要輸出的內容過濾
    this._stats = stats;
  }.bind(this));
Server.prototype._sendStats = function(sockets, stats, force) {
  if(!force &&
    stats &&
    (!stats.errors || stats.errors.length === 0) &&
    stats.assets &&
    stats.assets.every(function(asset) {
      return !asset.emitted;
      //每一個asset都是沒有emitted屬性,表示沒有發生變化。如果發生變化那麼這個assets肯定有emitted屬性。所以emitted屬性表示是否又重新生成了一遍assets資源
    })
  )
    return this.sockWrite(sockets, "still-ok");
  this.sockWrite(sockets, "hash", stats.hash);
  //正常情況下首先發送hash,然後傳送ok
  if(stats.errors.length > 0)
    this.sockWrite(sockets, "errors", stats.errors);
  else if(stats.warnings.length > 0)
    this.sockWrite(sockets, "warnings", stats.warnings);
  else
  //傳送hash後再發送ok
    this.sockWrite(sockets, "ok");
}

6.獲取stats中所有的modules

獲取compilation所有的modules:

compilation.getStats().toJson().modules

modules內部結構如下:

 { id: 10,
   identifier: 'C:\\Users\\Administrator\\Desktop\\webpack-chunkfilename\\node_
odules\\html-loader\\index.js!C:\\Users\\Administrator\\Desktop\\webpack-chunkf
lename\\src\\Components\\Header.html',
   name: './src/Components/Header.html',//模組名稱,已經轉化為相對於根目錄的路徑
   index: 10,
   index2: 8,
   size: 62,
   cacheable: true,//快取
   built: true,
   optional: false,
   prefetched: false,
   chunks: [ 0 ],//在那個chunk中出現
   assets: [],
   issuer: 'C:\\Users\\Administrator\\Desktop\\webpack-chunkfilename\\node_modu
es\\eslint-loader\\index.js!C:\\Users\\Administrator\\Desktop\\webpack-chunkfil
name\\src\\Components\\Header.js',//是誰開始本模組的呼叫的,即模組呼叫發起者
   issuerId: 1,//發起者id
   issuerName: './src/Components/Header.js',//發起者相對於根目錄的路徑
   profile: undefined,
   failed: false,
   errors: 0,
   warnings: 0,
   reasons: [ [Object] ],
   usedExports: [ 'default' ],
   providedExports: null,
   depth: 2,
   source: 'module.exports = "<header class=\\"header\\">{{text}}</header>";' }//source是模組內容

7.如何對輸出的assets資源進行過濾

下面給出一個例子1:

 var assets = {
    // The public path
    publicPath: publicPath,
    // Will contain all js & css files by chunk
    chunks: {},
    // Will contain all js files
    js: [],
    // Will contain all css files
    css: [],
    // Will contain the html5 appcache manifest files if it exists
    //這裡是application cache檔案,這裡不是檔案內容是檔案的名稱。key就是檔名稱
    manifest: Object.keys(compilation.assets).filter(function (assetFile) {
      return path.extname(assetFile) === '.appcache';
    })[0]
  };

例子2:過濾出所有的css檔案

 var chunkFiles = [].concat(chunk.files).map(function (chunkFile) {
      return publicPath + chunkFile;
    });
    var css = chunkFiles.filter(function (chunkFile) {
      // Some chunks may contain content hash in their names, for ex. 'main.css?1e7cac4e4d8b52fd5ccd2541146ef03f'.
      // We must proper handle such cases, so we use regexp testing here
      return /.css($|\?)/.test(chunkFile);
    });

當然過濾出來的資源可能會要新增hash,我們看看如何處理:

/**
 * Appends a cache busting hash
 self.appendHash(assets.manifest, webpackStatsJson.hash);
 為檔名稱後面新增一個hash值用於快取,是在檔案的路徑上而不是內容
 */
 var webpackStatsJson = compilation.getStats().toJson();
 if (this.options.hash) {
    assets.manifest = self.appendHash(assets.manifest, webpackStatsJson.hash);
    assets.favicon = self.appendHash(assets.favicon, webpackStatsJson.hash);
  }
HtmlWebpackPlugin.prototype.appendHash = function (url, hash) {
  if (!url) {
    return url;
  }
  return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
};

8.如何對輸出的資源路徑進行特別的處理

HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function (compilation, chunks) {
  var self = this;
  var webpackStatsJson = compilation.getStats().toJson();
  //獲取compilation的所有的資訊
  // Use the configured public path or build a relative path
  var publicPath = typeof compilation.options.output.publicPath !== 'undefined'
    // If a hard coded public path exists use it
    ? compilation.mainTemplate.getPublicPath({hash: webpackStatsJson.hash})
    // If no public path was set get a relative url path
    // 這個publicPath是可以使用hash的
    : path.relative(path.resolve(compilation.options.output.path, path.dirname(self.childCompilationOutputName)), compilation.options.output.path)
      .split(path.sep).join('/');
    //self.childCompilationOutputName得到的"index.html",dirname得到的是".",所以resolve結果為" C:\Users\Administrator\Desktop\webpack-chunkfilename\builds"
    if (publicPath.length && publicPath.substr(-1, 1) !== '/') {
      publicPath += '/';
    }
    //獲取倒數第一個字元,新增一個"/"

  var assets = {
    // The public path
    publicPath: publicPath,
    // Will contain all js & css files by chunk
    chunks: {},
    // Will contain all js files
    js: [],
    // Will contain all css files
    css: [],
    // Will contain the html5 appcache manifest files if it exists
    //這裡是application cache檔案,這裡不是檔案內容是檔案的名稱
    manifest: Object.keys(compilation.assets).filter(function (assetFile) {
      return path.extname(assetFile) === '.appcache';
    })[0]
  };

  // Append a hash for cache busting(快取清除)
  //hash: true | false if true then append a unique webpack compilation hash to all
  // included scripts and CSS files. This is useful for cache busting.
  if (this.options.hash) {
    assets.manifest = self.appendHash(assets.manifest, webpackStatsJson.hash);
    assets.favicon = self.appendHash(assets.favicon, webpackStatsJson.hash);
  }
  for (var i = 0; i < chunks.length; i++) {
    var chunk = chunks[i];
    var chunkName = chunk.names[0];
    //為每一個chunk都在上面的這個assets物件上新增一個物件,如assets.chunks[chunkName]={}
    assets.chunks[chunkName] = {};
    // Prepend the public path to all chunk files
    //chunk.files表示該chunk產生的所有的檔案,不過是檔名稱name而不是內容
    var chunkFiles = [].concat(chunk.files).map(function (chunkFile) {
      return publicPath + chunkFile;
    });

    // Append a hash for cache busting
    //為每一個檔案加上了publicPath同時還要加上hash
    if (this.options.hash) {
      chunkFiles = chunkFiles.map(function (chunkFile) {
        return self.appendHash(chunkFile, webpackStatsJson.hash);
      });
    }

    // Webpack outputs an array for each chunk when using sourcemaps
    // But we need only the entry file
    //chunk.files[0]就是該chunk產生的入口檔案
    var entry = chunkFiles[0];
    assets.chunks[chunkName].size = chunk.size;
    assets.chunks[chunkName].entry = entry;
    assets.chunks[chunkName].hash = chunk.hash;
    assets.js.push(entry);
    //為每一個該chunk產生的檔案都在上面的assets物件上新增一個物件,key是chunkName
    //value為一個物件{chunkName:{size:100,entry:'/qlin/',hash:'chunk的hash'}}
    // Gather all css files
    var css = chunkFiles.filter(function (chunkFile) {
      // Some chunks may contain content hash in their names, for ex. 'main.css?1e7cac4e4d8b52fd5ccd2541146ef03f'.
      // We must proper handle such cases, so we use regexp testing here
      return /.css($|\?)/.test(chunkFile);
    });
    assets.chunks[chunkName].css = css;
    //css屬性就是我們的檔案路徑
    assets.css = assets.css.concat(css);
  }
  // Duplicate css assets can occur on occasion if more than one chunk
  // requires the same css.
  assets.css = _.uniq(assets.css);
  //如果多個chunk使用了同一個css那麼會產生重複的css
  return assets;
};

上面的這個例子展示瞭如何如何為輸出的資源做這些處理:新增publicPath以便html能夠正常訪問;為資源的檔名,即URL中新增編譯的hash值。同時你要注意:

  var entry = chunkFiles[0];
    assets.chunks[chunkName].size = chunk.size;
    assets.chunks[chunkName].entry = entry;
    assets.chunks[chunkName].hash = chunk.hash;
    assets.js.push(entry);

之所以會如此處理是因為如果在webpack中配置了:

  devtool:'cheap-source-map',

那麼每一個chunk產生的files就是一個數組,如下:

[ 'builds/vendor.bundle.js', 'builds/vendor.bundle.js.map' ]
[ 'builds/bundle.js', 'builds/bundle.js.map' ]

所以我們只會儲存第一個js檔案而已!

9.如何判斷是否要重新產生資原始檔

 // If this is a hot update compilation, move on!
 // This solves a problem where an `index.html` file is generated for hot-update js files
 // It only happens in Webpack 2, where hot updates are emitted separately before the full bundle
  if (self.isHotUpdateCompilation(assets)) {
      return callback();
    }  // If the template and the assets did not change we don't have to emit the html
    //如果template和assets資源沒有發生變化,我們不會重新產生html
    var assetJson = JSON.stringify(self.getAssetFiles(assets));
    //注意:這裡包括了js,css,.appcache檔案以及publicPath路徑
    //isCompilationCached = compilationResult.hash && self.childCompilerHash === compilationResult.hash;
   //如果上次child compiler產生的hash和本次產生的hash(本次再次編譯了一遍html)一致表示html沒有變化
   //同時配置指定了可以讀取html快取,而且資源本身也沒有發生變化,直接返回
    if (isCompilationCached && self.options.cache && assetJson === self.assetJson) {
      return callback();
    } else {
      self.assetJson = assetJson;
    }

HtmlWebpackPlugin.prototype.isHotUpdateCompilation = function (assets) {
  //如果每一個js的檔名,也就是入口檔案都含有hot-update欄位那麼返回true
  return assets.js.length && assets.js.every(function (name) {
    return /\.hot-update\.js$/.test(name);
  });
};
HtmlWebpackPlugin.prototype.getAssetFiles = function (assets) {
  var files = _.uniq(Object.keys(assets).filter(function (assetType) {
    return assetType !== 'chunks' && assets[assetType];
    //獲取型別不是chunks的資源
  }).reduce(function (files, assetType) {
    return files.concat(assets[assetType]);
  }, []));
  files.sort();
  return files;
};

如果該html是為了達到熱更新的效果,那麼不用每次都重新產生html檔案,這有點值得學習。同時,這裡也展示瞭如何判斷本次編譯的資源和上次的資源有沒有發生變化,其通過把JSON轉化為字串的方式來比較!注意:childCompilerHash表示載入了html後得到的hash值!

10.如何獲取一次打包依賴的所有的檔案並在所有檔案中新增一個自己的檔案

/*
 * Pushes the content of the given filename to the compilation assets
 */
HtmlWebpackPlugin.prototype.addFileToAssets = function (filename, compilation) {
  filename = path.resolve(compilation.compiler.context, filename);
  //獲取檔案的絕對路徑,我們配置的是一個path
  //處理一個 promise 的 map 集合。只有有一個失敗,所有的執行都結束,也就是說我們必須要同時獲取到這個檔案的大小和內容本身
  return Promise.props({
    size: fs.statAsync(filename),
    source: fs.readFileAsync(filename)
  })
  .catch(function () {
    return Promise.reject(new Error('HtmlWebpackPlugin: could not load file ' + filename));
  })
  .then(function (results) {
    var basename = path.basename(filename);
    //path.basename('/foo/bar/baz/asdf/quux.html')
     // Returns: 'quux.html'
     //獲取檔名稱
    compilation.fileDependencies.push(filename);
    //在compilation.fileDependencies中新增檔案,filename是完整的檔案路徑
    compilation.assets[basename] = {
      source: function () {
        return results.source;
      },
      //source是檔案的內容,通過fs.readFileAsync完成
      size: function () {
        return results.size.size;
        //size通過 fs.statAsync(filename)完成
      }
    };
    return basename;
  });
};

這裡要學習的內容還是挺多的,首先把我們自己的檔案新增到需要打包依賴檔案集合中:

compilation.fileDependencies//在compilation.fileDependencies新增的必須是絕對路徑

接下來就是修改compilation.assets物件:

compilation.assets[basename] = {
      source: function () {
        return results.source;
      },
      //source是檔案的內容,通過fs.readFileAsync完成
      size: function () {
        return results.size.size;
        //size通過 fs.statAsync(filename)完成
      }
    };
    return basename;
  });

不過這裡的key就是我們的檔名,而不再包含檔案路徑!最後一個就是題外話了,來源於我們的bluebird(Promise.prop方法):

var Promise = require('bluebird');
Promise.promisifyAll(fs);//所有fs物件的方法都promisify了,所以才有fs.statAsync(filename), fs.readFileAsync(filename),通過這兩個方法就可以獲取到我們的compilation.assets需要的source和size

參考資料:

相關推薦

它們理解webpack

最新內容,請在github閱讀。同時,All issue and star welcomed! 1.獲取webpack配置的輸出配置 compilation.outputOptions 其中該物件有如下內容: { path: 'builds'

【Statement和PreparedStatement什麽區別?哪個性?預編譯語句,防止sql註入問題】

dstat () 驅動程序 對象 生成 from result 查詢語句 驅動 答:與Statement相比,①PreparedStatement接口代表預編譯的語句,它主要的優勢在於可以減少SQL的編譯錯誤並增加SQL的安全性(減少SQL註射攻擊的可能性);②Prepar

學會這套方法論,你高效地解決問題(3)

解決問題的基本步驟 曲凱是四十二章經創始人,他在剛畢業做諮詢師的時候就有個疑問,他的那些客戶都是大公司的大老闆,都有很豐富的行業資源和經驗,為什麼要找他一個剛畢業的人幫他們解決問題呢? 後來他發現,大老闆們跟管理諮詢師最大的區別是,面對問題,大老闆們用的是經驗,而諮詢師有一套思維

學會這套方法論,你高效地解決問題(2)

解決問題的基本步驟 曲凱是四十二章經創始人,他在剛畢業做諮詢師的時候就有個疑問,他的那些客戶都是大公司的大老闆,都有很豐富的行業資源和經驗,為什麼要找他一個剛畢業的人幫他們解決問題呢? 後來他發現,大老闆們跟管理諮詢師最大的區別是,面對問題,大老闆們用的是經驗,而諮詢師有一套思維

學會這套方法論,你高效地解決問題(1)

解決問題的基本步驟 曲凱是四十二章經創始人,他在剛畢業做諮詢師的時候就有個疑問,他的那些客戶都是大公司的大老闆,都有很豐富的行業資源和經驗,為什麼要找他一個剛畢業的人幫他們解決問題呢? 後來他發現,大老闆們跟管理諮詢師最大的區別是,面對問題,大老闆們用的是經驗,而諮詢師有一套思維

一些簡單的例子讓你在Java中的學習並理解迴圈結構(1)!

一、java中流程控制方式採用三種基本流程結構:順序結構,選擇(分支)結構,迴圈結構。   1、[if-else 結構]    if(1>2){     system.out.println("if條件成立時,執行的程式碼");   }else{     System.out.println("if條

群用戶通過微信小程序可以地協作

bug space 隱藏 針對 簡單 完整 script 寬度 table   今天,小程序向開發者開放了群ID的接口能力。簡單地說,就是當你把小程序分享在群聊中,被點擊後開發者可獲取群ID和群名稱,也方便更好地針對群場景提供個性化服務。不同的群有各自的群ID,那麽這個新能

什麽情況下用+運算符進行字符串連接比調用StringBuffer/StringBuilder對象的append方法連接字符串性

字符串拼接 build 字符串 字符串連接 操作 重新 運算 運算符 對象存儲 String一旦賦值或實例化後就不可更改,如果賦予新值將會重新開辟內存地址進行存儲。而StringBuffer類使用append和insert等方法改變字符串值時只是在原有對象存儲的內存地址上進

當社交大數據遇到心理學,靠朋友圈雲確診抑郁

分享 原因 CI -h 祝福語 發布 人工智能 學術 調查 在人工智能的發展過程中,AI+一切大數據似乎都能提升效率,得到喜聞樂見的結果。可唯獨有一樣,結合了AI之後會造成大量負面影響,這就是社交媒體數據。平時我們習慣了在社交媒體中表達情緒、表達興趣愛好甚至會在不經意間透露

用Javascript把CSDN給 “黑” ,JavaScript真的是無所不能

效果如下(這不是PS的): 當然這不是真的把CSDN給黑了,這是用了一點javascript技巧 如何實現: 開啟CSDN首頁,在位址列裡面輸入 javascript:document.body.contentEditable='true'; document.de

提升公司員工凝聚力,珠海拓展公司團建培訓課程,的幫你解決核心問題

在進行戶外拓展訓練的過程中,員工在成功體驗的同時體驗到了成功,這種成功來源於同伴的幫助與支援,會讓員工在團體中體會到一種歸宿的滿足感,會為所在的集體驕傲,會為自己所在這個集體而自豪。經過這樣的團隊建設後,會加強員工的凝聚力而使其流動率和流失率大大減少。 作為人力資源部的培訓人員真的要將培訓結

從身邊開源開始學習,用過才能理解程式碼

2015年12月20日,雲棲社群上線。2018年12月20日,雲棲社群3歲。 阿里巴巴常說“晴天修屋頂”。 在我們看來,寒冬中,最值得投資的是學習,是增厚的知識儲備。 所以社群特別製作了這個專輯——分享給開發者們20個彌足珍貴的成長感悟,50本書單。 多年以後,再回首2018-19年,留給我們自己的,

雲棲專輯 | 阿里開發者們的第3個感悟:從身邊開源開始學習,用過才能理解程式碼

2015年12月20日,雲棲社群上線。2018年12月20日,雲棲社群3歲。 阿里巴巴常說“晴天修屋頂”。 在我們看來,寒冬中,最值得投資的是學習,是增厚的知識儲備。 所以社群特別製作了這個專輯——分享給開發者們20個彌足珍貴的成長感悟,50本書單。 多年以後,再回首2018-19年,留給我們自

理解貝葉斯定律(Bayes Law)和卡曼濾波器(Kalman Filter)原理

在概率理論中,我們都學習過 貝葉斯理論: P(A|B) = P(A)P(B|A) / P(B)。它的意義在模式識別和卡曼濾波中是基礎。理解它,是學習高階演算法的前提。至於模式識別和卡曼濾波等很有價值的

趁“未老”,再認真一次,我覺得你

每一年的年尾,我都會問自己:這一年,我收穫了什麼?            你呢?                    

Android 簡單用又特效十足的粘性佈局CoordinatorLayout , 的解決螢幕適配問題

coordinatorlayout介紹:coordinatorlayout是5.0之後推出一個功能。使用佈局完成特效比較簡單。它像一個協調者,來協助一些屬性完成一些特效。appbarlayout,是一個可以滑動的控制元件,但是必須在coordinatorlayout裡面才可以

手機上的pycharm編輯器,只要你一部手機寫程式碼!分享給你

今天介紹一款手機上的pycharm編輯器:Pydroid 3. 手機上的pycharm編輯器,只要你有一部手機就能寫程式碼!分享給你 手機上的pycharm編輯器,只要你有一部手機就能寫程式碼!分享給你 在網上搜得到這軟體,可以下載來體驗,網上都是英文版本的,如果需要漢化版的

ALSA驅動分析,比ALSA官方文件理解

Linux ALSA音效卡驅動之一:ALSA架構簡介 http://blog.csdn.net/droidphone/article/details/6271122 Linux ALSA音效卡驅動之二:音效卡的建立 http://blog.csdn.net/droidpho

傳統企業IT架構如何的支撐企業網際網路業務的轉型

阿里巴巴董事局主席馬雲在2016年11月16號在烏鎮召開的第三屆世界網際網路大會開幕式上發表演講時提出了以下觀點: “最近兩百年三次技術革命,每次技術革命的週期都是大約50年,而且有一個規律,前20年是技術研發的革命,新技術層出不窮,一批批湧現;後30年進入技術應用期,新技術開始和傳統產業相結合,

SolidWorks sp0和sp1sp2sp3sp4sp5什麼區別?哪個

SolidWorks sp0和sp1sp2sp3sp4sp5有什麼區別?哪個更好? 對於SolidWorks瞭解的工程師可能會知道,那就是SolidWorks版本每年甚至每季度都會更新,這也正提現了SolidWorks官方公司對於SolidWorks使用效能的不懈追求,那麼SolidWorks2