0挨家挨户击破,深刻领会

By admin in 4858美高梅 on 2019年3月30日

目录

前言

最近在重拾 webpack 一些知识点,希望对前者模块化有更加多的精通,以前对
webpack 打包机制有所好奇,没有知道深远,浅尝则止,近年来透过对 webpack
打包后的公文实行查看,对其何等打包 JS
文件有了更深的精晓,希望由此那篇小说,能够支持读者你精晓:

  1. webpack 单文件怎么着进展包装?
  2. webpack 多文件怎样实行代码切割?
  3. webpack1 和 webpack2 在文书打包上有啥差别?
  4. webpack2 哪些成功 tree shaking?
  5. webpack3 咋做到 scope hoisting?

本文全体示例代码全体身处自家的 Github 上,看兴趣的能够看看:

git clone https://github.com/happylindz/blog.git
cd blog/code/webpackBundleAnalysis
npm install

深远领会 webpack 文件打包机制(小结),深远明白webpack

webpack用作前端最火的构建筑工程具,是前者自动化学工业具链最首要的部分,使用门槛较高。本连串是小编自个儿的上学记录,相比基础,希望通过难点+
化解措施
的方式,从前端创设中境遇的切实可行供给为落脚点,学习webpack工具中相应的拍卖方法。(本篇中的参数配置及利用方法均基于webpack4.0版本

  • 一.
    Js模块化开发
  • 二.
    Js文件的形似打包须要
  • 三.
    使用webpack处理js文件

    • 3.1
      使用babel转换ES6+语法
    • 3.2
      脚本合并
    • 3.3
      公共模块识别
    • 3.4
      代码分割
    • 3.5
      代码混淆压缩
  • 四.
    细说splitChunks技术

    • 4.1
      参数表达
    • 4.2
      参数配置
    • 4.3
      代码分割实例
  • 五.
    参考及附属类小部件表达

webpack 单文件怎样打包?

率先现在 webpack 作为当前主流的前端模块化学工业具,在 webpack
刚初始风靡的时候,大家平常通过 webpack 将持有拍卖公事全部打包成三个bundle 文件, 先通过2个归纳的例证来看:

// src/single/index.js
var index2 = require('./index2');
var util = require('./util');
console.log(index2);
console.log(util);

// src/single/index2.js
var util = require('./util');
console.log(util);
module.exports = "index 2";

// src/single/util.js
module.exports = "Hello World";

// 通过 config/webpack.config.single.js 打包
const webpack = require('webpack');
const path = require('path')

module.exports = {
 entry: {
 index: [path.resolve(__dirname, '../src/single/index.js')],
 },
 output: {
 path: path.resolve(__dirname, '../dist'),
 filename: '[name].[chunkhash:8].js'
 },
}

经过 npm run build:single 可阅览打包效果,打包内容差不多如下(经过简要):

// dist/index.xxxx.js
(function(modules) {
 // 已经加载过的模块
 var installedModules = {};

 // 模块加载函数
 function __webpack_require__(moduleId) {
 if(installedModules[moduleId]) {
  return installedModules[moduleId].exports;
 }
 var module = installedModules[moduleId] = {
  i: moduleId,
  l: false,
  exports: {}
 };
 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
 module.l = true;
 return module.exports;
 }
 return __webpack_require__(__webpack_require__.s = 3);
})([
/* 0 */
(function(module, exports, __webpack_require__) {
 var util = __webpack_require__(1);
 console.log(util);
 module.exports = "index 2";
}),
/* 1 */
(function(module, exports) {
 module.exports = "Hello World";
}),
/* 2 */
(function(module, exports, __webpack_require__) {
 var index2 = __webpack_require__(0);
 index2 = __webpack_require__(0);
 var util = __webpack_require__(1);
 console.log(index2);
 console.log(util);
}),
/* 3 */
(function(module, exports, __webpack_require__) {
 module.exports = __webpack_require__(2);
})]);

将相对无关的代码剔除掉后,剩下首要的代码:

  1. 第③ webpack
    将兼具模块(能够简不难单明了成文件)包裹于一个函数中,并传播默许参数,那里有七个公文再添加三个输入模块一共多少个模块,将它们放入三个数组中,取名为
    modules,并由此数组的下标来作为 moduleId。
  2. 将 modules 传入三个自进行函数中,自推行函数中带有三个installedModules
    已经加载过的模块和一个模块加载函数,最后加载入口模块并重临。
  3. __webpack_require__ 模块加载,先判断 installedModules
    是还是不是已加载,加载过了就径直回到 exports 数据,没有加载过该模块就通过
    modules[moduleId].call(module.exports, module, module.exports,
    __webpack_require__) 执行模块并且将 module.exports 给重返。

很简短是否,有个别点须要小心的是:

  1. 每种模块 webpack
    只会加载2回,所以再度加载的模块只会实施一遍,加载过的模块会停放
    installedModules,下次亟待供给该模块的值就直接从里边拿了。
  2. 模块的 id
    直接通过数组下标去各样对应的,那样能保障不难且唯一,通过其余措施比如文件名或文件路径的主意就相比较费心,因为文件名恐怕出现重名,不唯一,文件路径则会叠加文件体量,并且将路径暴露给前端,不够安全。
  3. modules[moduleId].call(module.exports, module, module.exports,
    __webpack_0挨家挨户击破,深刻领会。require__) 有限支撑了模块加载时 this 的指向
    module.exports 并且传入默许参数,很简短,可是多解释。

前言

前不久在重拾 webpack 一些知识点,希望对前者模块化有越多的接头,在此在此之前对
webpack 打包机制有所好奇,没有明了深远,浅尝则止,方今透过对 webpack
打包后的文本实行查看,对其怎么样打包 JS
文件有了更深的精晓,希望通过那篇文章,能够支持读者你知道:

  1. webpack 单文件怎么样开始展览打包?
  2. webpack 多文本如何进展代码切割?
  3. webpack1 和 webpack2 在文书打包上有啥界别?
  4. webpack2 怎样形成 tree shaking?
  5. webpack3 怎么样实现 scope hoisting?

正文全体示例代码全部位于笔者的 Github 上,看兴趣的能够看看:

git clone https://github.com/happylindz/blog.git
cd blog/code/webpackBundleAnalysis
npm install

4858美高梅 1

webpack用作前端最火的营造筑工程具,是前者自动化学工业具链最要害的有的,使用门槛较高。本种类是小编本人的读书记录,相比基础,希望经过题材
+
消除办法
的格局,从前端营造中境遇的求实供给为着眼点,学习webpack工具中相应的拍卖方法。(本篇中的参数配置及利用方式均基于webpack4.0版本

本篇摘要:

本篇首要介绍基于webpack4.0splitChunks富含技术。

webpack 多文件怎样进展代码切割?

webpack
单文件打包的方法应付一些简约场景就够用了,不过大家在开发一些错综复杂的使用,即便没有对代码举办切割,将第③方库(jQuery)或框架(React)和事情代码全体装进在一块儿,就会导致用户访问页面速度极慢,不能管用使用缓存,你的业主可能就要找你开口了。

那正是说 webpack 多文件输入怎么样举行代码切割,让作者先写1个简易的例子:

// src/multiple/pageA.js
const utilA = require('./js/utilA');
const utilB = require('./js/utilB');
console.log(utilA);
console.log(utilB);

// src/multiple/pageB.js
const utilB = require('./js/utilB');
console.log(utilB);
// 异步加载文件,类似于 import()
const utilC = () => require.ensure(['./js/utilC'], function(require) {
 console.log(require('./js/utilC'))
});
utilC();

// src/multiple/js/utilA.js 可类比于公共库,如 jQuery
module.exports = "util A";

// src/multiple/js/utilB.js
module.exports = 'util B';

// src/multiple/js/utilC.js
module.exports = "util C";

那边大家定义了四个入口 pageA 和 pageB 和五个库
util,大家意在代码切割做到:

  1. 因为两入口都以用到了
    utilB,我们意在把它抽离成单身文件,并且当用户访问 pageA 和 pageB
    的时候都能去加载 utilB 那么些集人体模型块,而不是存在于分别的入口文件中。
  2. pageB 中 utilC 不是页面一早先加载时候就供给的始末,要是 utilC
    相当大,大家不期待页面加载时就直接加载
    utilC,而是当用户达到某种条件(如:点击按钮)才去异步加载
    utilC,那时候我们须要将 utilC
    抽离成独立文件,当用户必要的时候再去加载该公文。

那么 webpack 须求怎么安顿呢?

// 通过 config/webpack.config.multiple.js 打包
const webpack = require('webpack');
const path = require('path')

module.exports = {
 entry: {
 pageA: [path.resolve(__dirname, '../src/multiple/pageA.js')],
 pageB: path.resolve(__dirname, '../src/multiple/pageB.js'),
 },
 output: {
 path: path.resolve(__dirname, '../dist'),
 filename: '[name].[chunkhash:8].js',
 },
 plugins: [
 new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: 2,
 }),
 new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  chunks: ['vendor']
 })
 ]
}

单单配置多 entry 是不够的,那样只会扭转七个 bundle 文件,将 pageA 和
pageB
所需求的始末全方位放入,跟单入口文件并从未区分,要实现代码切割,大家供给依赖webpack 内置的插件 CommonsChunkPlugin。

先是 webpack
执行存在一些周转时代码,即一部分开首化的工作,就如以前单文件中的
__webpack_require__
,那部分代码必要加载于具有文件在此之前,也就是开首化学工业作,少了那有的开端化代码,后边加载过来的代码就不可能分辨并工作了。

new webpack.optimize.CommonsChunkPlugin({
 name: 'vendor',
 minChunks: 2,
})

那段代码的意义是,在这几个进口文件中,找到那个引用五次的模块(如:utilB),帮自身抽离成1个叫
vendor 文件,此时那某个初叶化学工业作的代码会被抽离到 vendor 文件中。

new webpack.optimize.CommonsChunkPlugin({
 name: 'manifest',
 chunks: ['vendor'],
 // minChunks: Infinity // 可写可不写
})

那段代码的含义是在 vendor 文件中帮本身把起始化代码抽离到 mainifest
文件中,此时 vendor 文件中就只剩下 utilB
这几个模块了。你或然会惊叹为何要如此做?

因为这么能够给 vendor 生成平安的 hash
值,每便修改工作代码(pageA),那段起首化时代码就会发生变化,那么只要将那段开始化代码放在
vendor 文件中的话,每一遍都会变卦新的
vendor.xxxx.js,那样不方便人民群众持久化缓存,假如不清楚也没提到,下次小编会其它写一篇作品来叙述那部分情节。

除此以外 webpack 私下认可会抽离异步加载的代码,那一个不须求您做额外的布局,pageB
中异步加载的 utilC 文件会直接抽离为 chunk.xxxx.js 文件。

由此此时大家页面加载文件的逐一就会成为:

mainifest.xxxx.js // 初始化代码
vendor.xxxx.js // pageA 和 pageB 共同用到的模块,抽离
pageX.xxxx.js  // 业务代码 
当 pageB 需要 utilC 时候则异步加载 utilC

推行 npm run build:multiple 即可查看包装内容,首先来看下 manifest
如何做开头化学工业作(精简版)?

// dist/mainifest.xxxx.js
(function(modules) { 
 window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
 var moduleId, chunkId, i = 0, callbacks = [];
 for(;i < chunkIds.length; i++) {
  chunkId = chunkIds[i];
  if(installedChunks[chunkId])
  callbacks.push.apply(callbacks, installedChunks[chunkId]);
  installedChunks[chunkId] = 0;
 }
 for(moduleId in moreModules) {
  if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
  modules[moduleId] = moreModules[moduleId];
  }
 }
 while(callbacks.length)
  callbacks.shift().call(null, __webpack_require__);
 if(moreModules[0]) {
  installedModules[0] = 0;
  return __webpack_require__(0);
 }
 };
 var installedModules = {};
 var installedChunks = {
 4:0
 };
 function __webpack_require__(moduleId) {
 // 和单文件一致
 }
 __webpack_require__.e = function requireEnsure(chunkId, callback) {
 if(installedChunks[chunkId] === 0)
  return callback.call(null, __webpack_require__);
 if(installedChunks[chunkId] !== undefined) {
  installedChunks[chunkId].push(callback);
 } else {
  installedChunks[chunkId] = [callback];
  var head = document.getElementsByTagName('head')[0];
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.charset = 'utf-8';
  script.async = true;
  script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"pageA","1":"pageB","3":"vendor"}[chunkId]||chunkId) + "." + {"0":"e72ce7d4","1":"69f6bbe3","2":"9adbbaa0","3":"53fa02a7"}[chunkId] + ".js";
  head.appendChild(script);
 }
 };
})([]);

与单文件内容相同,定义了1个自实施函数,因为它不带有其余模块,所以传入1个空数组。除了定义了
__webpack_require__ ,还其它定义了多少个函数用来举行加载模块。

首先讲解代码前要求通晓四个概念,分别是 module 和 chunk

  1. chunk 代表生成后 js 文件,三个 chunkId 对应多少个打包好的 js
    文件(一共八个),从那段代码能够看出,manifest 的 chunkId 为
    4,并且从代码中还能看到:0-3 分别对应 pageA, pageB, 异步 utilC,
    vendor 公共模块文件,那也正是我们为什么不能够将那段代码放在 vendor
    的案由,因为文件的 hash 值会变。内容变了,vendor 生成的 hash
    值也就变了。
  2. module 对应着模块,能够归纳明了为包装前每一个 js
    文件对应一个模块,相当于事先 __webpack_require__
    加载的模块,同样的选取数组下标作为 moduleId 且是唯一不重复的。

那么为何要有别于 chunk 和 module 呢?

先是选择 installedChunks 来保存各种 chunkId
是还是不是被加载过,要是被加载过,则表明该 chunk 中所包含的模块已经被内置了
modules 中,注意是 modules 而不是 installedModules。大家先来大概看一下
vendor chunk 打包出来的内容。

// vendor.xxxx.js
webpackJsonp([3,4],{
 3: (function(module, exports) {
 module.exports = 'util B';
 })
});

在实行完 manifest 后就会先实施 vendor 文件,结合地点 webpackJsonp
的定义,大家能够驾驭 [3, 4] 代表 chunkId,当加载到 vendor
文件后,installedChunks[3] 和 installedChunks[4] 将会被置为
0,那标志 chunk3,chunk4 已经被加载过了。

webpackJsonpCallback 一共有四个参数,chuckIds 一般包蕴该 chunk
文件正视的 chunkId 以及小编 chunkId,moreModules 代表该 chunk
文件带来新的模块。

var moduleId, chunkId, i = 0, callbacks = [];
for(;i < chunkIds.length; i++) {
 chunkId = chunkIds[i];
 if(installedChunks[chunkId])
 callbacks.push.apply(callbacks, installedChunks[chunkId]);
 installedChunks[chunkId] = 0;
}
for(moduleId in moreModules) {
 if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
 modules[moduleId] = moreModules[moduleId];
 }
}
while(callbacks.length)
 callbacks.shift().call(null, __webpack_require__);
if(moreModules[0]) {
 installedModules[0] = 0;
 return __webpack_require__(0);
}

归纳说说 webpackJsonpCallback 做了怎么事,首先判断 chunkIds 在
installedChunks 里有没有回调函数函数未执行完,有的话则停放 callbacks
里,并且等下统一举行,并将 chunkIds 在 installedChunks 中全体置为 0,
然后将 moreModules 合并到 modules。

那中间唯有 modules[0] 是不稳定的,其它 modules
下标都是绝无仅有的,在卷入的时候 webpack 已经为它们统一号码,而 0
则为进口文件即 pageA,pageB 各有1个 module[0]。

接下来将 callbacks
执行并清空,保险了该模块加载初始前所从前置正视内容已经加载完结,最终判断
moreModules[0], 有值表达该公文为进口文件,则开端推行入口模块 0。

上边表达了一大堆,可是像 pageA 这种共同加载 manifest, vendor 以及 pageA
文件来说,每趟加载的时候 callbacks 都以为空的,因为它们在
installedChunks 中的值要嘛为 undefined(未加载), 要嘛为
0(已被加载)。installedChunks[chunkId] 的值永远为
false,所以在那种景色下 callbacks
里常有不会现出函数,借使仅仅是考虑这么的场合,上边的
webpackJsonpCallback 完全可以写成下边那样:

var moduleId, chunkId, i = 0, callbacks = [];
for(;i < chunkIds.length; i++) {
 chunkId = chunkIds[i];
 installedChunks[chunkId] = 0;
}
for(moduleId in moreModules) {
 if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
 modules[moduleId] = moreModules[moduleId];
 }
}
if(moreModules[0]) {
 installedModules[0] = 0;
 return __webpack_require__(0);
}

可是考虑到异步加载 js 文件的时候(比如 pageB 异步加载 utilC
文件),就没那么粗略,我们先来看下 webpack 是什么加载异步脚本的:

// 异步加载函数挂载在 __webpack_require__.e 上
__webpack_require__.e = function requireEnsure(chunkId, callback) {
 if(installedChunks[chunkId] === 0)
 return callback.call(null, __webpack_require__);

 if(installedChunks[chunkId] !== undefined) {
 installedChunks[chunkId].push(callback);
 } else {
 installedChunks[chunkId] = [callback];
 var head = document.getElementsByTagName('head')[0];
 var script = document.createElement('script');
 script.type = 'text/javascript';
 script.charset = 'utf-8';
 script.async = true;

 script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"pageA","1":"pageB","3":"vendor"}[chunkId]||chunkId) + "." + {"0":"e72ce7d4","1":"69f6bbe3","2":"9adbbaa0","3":"53fa02a7"}[chunkId] + ".js";
 head.appendChild(script);
 }
};

粗粗分为二种意况,(已经加载过,正在加载中以及没有加载过)

  1. 早就加载过该 chunk 文件,那就绝不再重新加载该 chunk
    了,直接实施回调函数即可,能够理解为假设页面有二种操作须要加载加载异步脚本,但是多个本子都依靠于公私模块,那么第②遍加载的时候发现前面率先次操作已经加载过了该
    chunk,则不用再去取得异步脚本了,因为该集人体模型块已经被实施过了。
  2. 一向不加载过,则动态地去插入 script 脚本去央浼 js
    文件,那也就怎么取名 webpackJsonpCallback ,因为跟 jsonp
    的思想很相近,所以那种异步加载脚本在做脚本错误监控时常常出现 Script
    error,具体原因能够查看本人前边写的文章:前者代码相当监控实战
  3. 正在加载中象征该 chunk
    文件已经在加载中了,比如说点击按钮触发异步脚本,用户点太快了,连点五次就也许出现这种场地,此时将回调函数放入
    installedChunks。

大家通过 utilC 生成的 chunk 来拓展教学:

webpackJsonp([2,4],{
 4: (function(module, exports) {
 module.exports = "util C";
 })
});

pageB 必要异步加载那么些 chunk:

webpackJsonp([1,4],[
/* 0 */
 (function(module, exports, __webpack_require__) {
 const utilB = __webpack_require__(3);
 console.log(utilB);
 const utilC = () => __webpack_require__.e/* nsure */(2, function(require) {
  console.log(__webpack_require__(4))
 });
 utilC();
 })
]);

当 pageB 举行某种操作供给加载 utilC 时就会执行
__webpack_require__.e(2, callback) 2,代表要求加载的模块
chunkId(utilC),异步加载 utilC 并将 callback 添加到 installedChunks[2]
中,然后当 utilC 的 chunk 文件加载完成后,chunkIds 包蕴 2,发现
installedChunks[2] 是个数组,里面还有此前还未执行的 callback 函数。

既然如此那样,那本人就将作者自个儿带来的模块先放到 modules
中,然后再统一执行在此以前未履行完的 callbacks 函数,那里指的是存放在于
installedChunks[2] 中的回调函数
(或许存在三个),那也正是验证那里的先后顺序:

// 先将 moreModules 合并到 modules, 再去执行 callbacks, 不然之前未执行的 callback 依赖于新来的模块,你不放进 module 我岂不是得不到想要的模块
for(moduleId in moreModules) {
 if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
 modules[moduleId] = moreModules[moduleId];
 }
}
while(callbacks.length)
 callbacks.shift().call(null, __webpack_require__);

webpack 单文件怎样打包?

第③现在 webpack 作为当前主流的前端模块化学工业具,在 webpack
刚初始流行的时候,大家日常通过 webpack 将装有拍卖公事全体打包成三个bundle 文件, 先通过贰个回顾的事例来看:

// src/single/index.js
var index2 = require('./index2');
var util = require('./util');
console.log(index2);
console.log(util);

// src/single/index2.js
var util = require('./util');
console.log(util);
module.exports = "index 2";

// src/single/util.js
module.exports = "Hello World";

// 通过 config/webpack.config.single.js 打包
const webpack = require('webpack');
const path = require('path')

module.exports = {
 entry: {
 index: [path.resolve(__dirname, '../src/single/index.js')],
 },
 output: {
 path: path.resolve(__dirname, '../dist'),
 filename: '[name].[chunkhash:8].js'
 },
}

透过 npm run build:single 可观望打包效果,打包内容大约如下(经过容易):

// dist/index.xxxx.js
(function(modules) {
 // 已经加载过的模块
 var installedModules = {};

 // 模块加载函数
 function __webpack_require__(moduleId) {
 if(installedModules[moduleId]) {
  return installedModules[moduleId].exports;
 }
 var module = installedModules[moduleId] = {
  i: moduleId,
  l: false,
  exports: {}
 };
 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
 module.l = true;
 return module.exports;
 }
 return __webpack_require__(__webpack_require__.s = 3);
})([
/* 0 */
(function(module, exports, __webpack_require__) {
 var util = __webpack_require__(1);
 console.log(util);
 module.exports = "index 2";
}),
/* 1 */
(function(module, exports) {
 module.exports = "Hello World";
}),
/* 2 */
(function(module, exports, __webpack_require__) {
 var index2 = __webpack_require__(0);
 index2 = __webpack_require__(0);
 var util = __webpack_require__(1);
 console.log(index2);
 console.log(util);
}),
/* 3 */
(function(module, exports, __webpack_require__) {
 module.exports = __webpack_require__(2);
})]);

将相对毫无干系的代码剔除掉后,剩下主要的代码:

  1. 先是 webpack
    将具有模块(能够不难驾驭成文件)包裹于多个函数中,并传到默许参数,那里有四个文件再增进3个进口模块一共多个模块,将它们放入二个数组中,取名为
    modules,并通过数组的下标来作为 moduleId。
  2. 将 modules 传入1个自推行函数中,自实施函数中富含三个installedModules
    已经加载过的模块和八个模块加载函数,最后加载入口模块并赶回。
  3. __webpack_require__ 模块加载,先判断 installedModules
    是不是已加载,加载过了就直接回到 exports 数据,没有加载过该模块就透过
    modules[moduleId].call(module.exports, module, module.exports,
    __webpack_require__) 执行模块并且将 module.exports 给再次回到。

很不难是还是不是,某些点需求注意的是:

  1. 每一个模块 webpack
    只会加载贰遍,所以重复加载的模块只会履行二遍,加载过的模块会停放
    installedModules,下次供给须要该模块的值就一贯从内部拿了。
  2. 模块的 id
    直接通过数组下标去挨家挨户对应的,那样能保险简单且唯一,通过此外措施比如文件名或文件路径的艺术就相比较费心,因为文件名也许出现重名,不唯一,文件路径则会叠加文件容量,并且将路径揭露给前端,不够安全。
  3. modules[moduleId].call(module.exports, module, module.exports,
    __webpack_require__) 保障了模块加载时 this 的指向
    module.exports 并且传入默许参数,很简短,可是多解释。

一. webpack中的html

对此浏览器而言,html文本是用户访问的入口点,也是兼备能源的挂载点,全数能源都以因而html中的标记来开始展览引用的。而在webpack的创设世界里,html只是1位作品显示板,而entry参数中钦命的javascript输入文件才是当真在构建进程中管理和调度财富的挂载点,html文件中最后体现的剧情,都以webpack在加工并为全数能源打好标志今后传递给它的,产业界将那种不同与浏览器的情势称之为“webpack的逆向注入”

4858美高梅 2

webpack1 和 webpack2 在文件打包上有啥样界别?

经过自家对包裹文件的体察,从 webpack1 到 webpack2
在卷入文件上有下边这么些重点的改变:

首先,moduleId[0] 不再为输入实施函数做保留,所以说并非傻傻看到
moduleId[0] 就觉得是包裹文件的进口模块,取而代之的是
window[“webpackJsonp”] = function webpackJsonpCallback(chunkIds,
moreModules, executeModules) {} 传入了第多少个参数
executeModules,是个数组,假如参数存在则注脚它是进口模块,然后就去执行该模块。

if(executeModules) {
 for(i=0; i < executeModules.length; i++) {
 result = __webpack_require__(__webpack_require__.s = executeModules[i]);
 }
}

其次,webpack2 中会暗许加载 OccurrenceOrderPlugin 那个插件,即你绝不
plugins 中丰盛这些布局它也会暗中同意执行,那它有如何用场呢?首若是在
webpack1 中 moduleId 的不分明性导致的,在 webpack1 中 moduleId
取决于引入文件的次第,那就会招致这些 moduleId 大概会常常产生变化, 而
OccurrenceOrderPlugin
插件会按引入次数最多的模块举行排序,引入次数的模块的 moduleId
越小,比如说上边引用的 utilB 模块引用次数为 2(最多),所以它的 moduleId
为 0。

webpackJsonp([3],[
/* 0 */
 (function(module, exports) {
 module.exports = 'util B';
 })
]);

最终说下在异步加载模块时, webpack2 是根据 Promise
的,所以说只要您要同盟低版本浏览器,必要引入 Promise-polyfill
,其余为引入请求添加了错误处理。

__webpack_require__.e = function requireEnsure(chunkId) {
 var promise = new Promise(function(resolve, reject) {
 installedChunkData = installedChunks[chunkId] = [resolve, reject];
 });
 installedChunkData[2] = promise;
 // start chunk loading
 var head = document.getElementsByTagName('head')[0];
 var script = document.createElement('script');
 script.type = 'text/javascript';
 script.charset = 'utf-8';
 script.async = true;
 script.timeout = 120000;
 script.src = __webpack_require__.p + "" + chunkId + "." + {"0":"ae9c5f5f","1":"0ac69acb","2":"20651a9c","3":"0cdc6c84"}[chunkId] + ".js";
 var timeout = setTimeout(onScriptComplete, 120000);
 script.onerror = script.onload = onScriptComplete;
 function onScriptComplete() {
 // 防止内存泄漏
 script.onerror = script.onload = null;
 clearTimeout(timeout);
 var chunk = installedChunks[chunkId];
 if(chunk !== 0) {
  if(chunk) {
  chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
  }
  installedChunks[chunkId] = undefined;
 }
 };
 head.appendChild(script);
 return promise;
};

可以看到,原本基于回调函数的艺术已经成为基于 Promise
做异步处理,其余添加了 onScriptComplete 用于做脚本加载退步处理。

在 webpack1
的时候,假诺由于网络原因当你加载脚本失利后,即便互连网复苏了,你重新展开某种操作需求同个
chunk 时候都会不得要领,首要缘由是战败之后没把 installedChunks[chunkId] =
undefined; 导致随后不会再对该 chunk 文件发起异步请求。

而在 webpack2 中,当脚本请求超时了(2min)或然加载战败,会将
installedChunks[chunkId] 清空,当下次再一次请求该 chunk
文件会再也加载,升高了页面包车型客车容错性。

那些是自作者在卷入文件中来看首要的分别,难免存有遗漏,借使您有更加多的见识,欢迎在评论区留言。

webpack 多文本怎么着进展代码切割?

webpack
单文件打包的法子应付一些粗略场景就够用了,可是大家在开发一些繁杂的选择,假若没有对代码进行切割,将第贰方库(jQuery)或框架(React)和事务代码全体装进在联合署名,就会导致用户访问页面速度一点也不快,不可能有效使用缓存,你的高管娘恐怕就要找你说话了。

那么 webpack 多文件输入怎么着进展代码切割,让自家先写三个大致的例证:

// src/multiple/pageA.js
const utilA = require('./js/utilA');
const utilB = require('./js/utilB');
console.log(utilA);
console.log(utilB);

// src/multiple/pageB.js
const utilB = require('./js/utilB');
console.log(utilB);
// 异步加载文件,类似于 import()
const utilC = () => require.ensure(['./js/utilC'], function(require) {
 console.log(require('./js/utilC'))
});
utilC();

// src/multiple/js/utilA.js 可类比于公共库,如 jQuery
module.exports = "util A";

// src/multiple/js/utilB.js
module.exports = 'util B';

// src/multiple/js/utilC.js
module.exports = "util C";

那里大家定义了四个入口 pageA 和 pageB 和两个库
util,大家希望代码切割做到:

  1. 因为两入口都以用到了
    utilB,大家意在把它抽离成单身文件,并且当用户访问 pageA 和 pageB
    的时候都能去加载 utilB 那几个集人体模型块,而不是存在于分其他入口文件中。
  2. pageB 中 utilC 不是页面一开始加载时候就要求的始末,假使 utilC
    非常大,大家不指望页面加载时就径直加载
    utilC,而是当用户高达某种条件(如:点击按钮)才去异步加载
    utilC,那时候大家需求将 utilC
    抽离成单身文件,当用户须要的时候再去加载该文件。

那正是说 webpack 要求怎么布署呢?

// 通过 config/webpack.config.multiple.js 打包
const webpack = require('webpack');
const path = require('path')

module.exports = {
 entry: {
 pageA: [path.resolve(__dirname, '../src/multiple/pageA.js')],
 pageB: path.resolve(__dirname, '../src/multiple/pageB.js'),
 },
 output: {
 path: path.resolve(__dirname, '../dist'),
 filename: '[name].[chunkhash:8].js',
 },
 plugins: [
 new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: 2,
 }),
 new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  chunks: ['vendor']
 })
 ]
}

唯有配置多 entry 是不够的,那样只会生成多个 bundle 文件,将 pageA 和
pageB
所必要的内容总体放入,跟单入口文件并不曾不同,要成功代码切割,咱们供给借助
webpack 内置的插件 康芒斯ChunkPlugin。

第① webpack
执行存在有的周转时期码,即一部分早先化的行事,就像从前单文件中的
__webpack_require__
,那部分代码须求加载于全体文件在此以前,约等于伊始化学工业作,少了这有的开头化代码,后边加载过来的代码就不能够甄别并工作了。

new webpack.optimize.CommonsChunkPlugin({
 name: 'vendor',
 minChunks: 2,
})

那段代码的含义是,在那几个进口文件中,找到那多少个引用三次的模块(如:utilB),帮本身抽离成1个叫
vendor 文件,此时那有个别初叶化学工业作的代码会被抽离到 vendor 文件中。

new webpack.optimize.CommonsChunkPlugin({
 name: 'manifest',
 chunks: ['vendor'],
 // minChunks: Infinity // 可写可不写
})

那段代码的意思是在 vendor 文件中帮小编把伊始化代码抽离到 mainifest
文件中,此时 vendor 文件中就只剩余 utilB
这一个模块了。你恐怕会惊叹为啥要如此做?

因为这么能够给 vendor 生成平安的 hash
值,每一次修改工作代码(pageA),那段初步化时期码就会发生变化,那么一旦将那段最先化代码放在
vendor 文件中的话,每回都会生成新的
vendor.xxxx.js,那样不便民持久化缓存,若是不掌握也没提到,下次作者会其余写一篇小说来描述那部分剧情。

别的 webpack 默许会抽离异步加载的代码,这么些不供给你做额外的配置,pageB
中异步加载的 utilC 文件会直接抽离为 chunk.xxxx.js 文件。

从而那时候大家页面加载文件的依次就会化为:

mainifest.xxxx.js // 初始化代码
vendor.xxxx.js // pageA 和 pageB 共同用到的模块,抽离
pageX.xxxx.js  // 业务代码 
当 pageB 需要 utilC 时候则异步加载 utilC

举行 npm run build:multiple 即可查看包装内容,首先来看下 manifest
如何做开头化学工业作(精简版)?

// dist/mainifest.xxxx.js
(function(modules) { 
 window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
 var moduleId, chunkId, i = 0, callbacks = [];
 for(;i < chunkIds.length; i++) {
  chunkId = chunkIds[i];
  if(installedChunks[chunkId])
  callbacks.push.apply(callbacks, installedChunks[chunkId]);
  installedChunks[chunkId] = 0;
 }
 for(moduleId in moreModules) {
  if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
  modules[moduleId] = moreModules[moduleId];
  }
 }
 while(callbacks.length)
  callbacks.shift().call(null, __webpack_require__);
 if(moreModules[0]) {
  installedModules[0] = 0;
  return __webpack_require__(0);
 }
 };
 var installedModules = {};
 var installedChunks = {
 4:0
 };
 function __webpack_require__(moduleId) {
 // 和单文件一致
 }
 __webpack_require__.e = function requireEnsure(chunkId, callback) {
 if(installedChunks[chunkId] === 0)
  return callback.call(null, __webpack_require__);
 if(installedChunks[chunkId] !== undefined) {
  installedChunks[chunkId].push(callback);
 } else {
  installedChunks[chunkId] = [callback];
  var head = document.getElementsByTagName('head')[0];
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.charset = 'utf-8';
  script.async = true;
  script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"pageA","1":"pageB","3":"vendor"}[chunkId]||chunkId) + "." + {"0":"e72ce7d4","1":"69f6bbe3","2":"9adbbaa0","3":"53fa02a7"}[chunkId] + ".js";
  head.appendChild(script);
 }
 };
})([]);

与单文件内容相同,定义了一个自推行函数,因为它不包罗别的模块,所以传入三个空数组。除了定义了
__webpack_require__ ,还其它定义了多个函数用来进展加载模块。

首先讲解代码前须求通晓七个概念,分别是 module 和 chunk

  1. chunk 代表生成后 js 文件,一个 chunkId 对应1个打包好的 js
    文件(一共两个),从那段代码能够看出,manifest 的 chunkId 为
    4,并且从代码中仍是能够看到:0-3 分别对应 pageA, pageB, 异步 utilC,
    vendor 公共模块文件,那约等于大家为何不能够将那段代码放在 vendor
    的原故,因为文件的 hash 值会变。内容变了,vendor 生成的 hash
    值也就变了。
  2. module 对应着模块,可以简不难单领会为包装前每种 js
    文件对应2个模块,也正是以前 __webpack_require__
    加载的模块,同样的使用数组下标作为 moduleId 且是绝无仅有不另行的。

那正是说为何要区分 chunk 和 module 呢?

首先使用 installedChunks 来保存种种 chunkId
是还是不是被加载过,若是被加载过,则印证该 chunk 中所包涵的模块已经被平放了
modules 中,注意是 modules 而不是 installedModules。大家先来简单看一下
vendor chunk 打包出来的剧情。

// vendor.xxxx.js
webpackJsonp([3,4],{
 3: (function(module, exports) {
 module.exports = 'util B';
 })
});

在实践完 manifest 后就会先实施 vendor 文件,结合方面 webpackJsonp
的定义,我们得以明白 [3, 4] 代表 chunkId,当加载到 vendor
文件后,installedChunks[3] 和 installedChunks[4] 将会被置为
0,那申明 chunk3,chunk4 已经被加载过了。

webpackJsonpCallback 一共有七个参数,chuckIds 一般包含该 chunk
文件依赖的 chunkId 以及本人 chunkId,moreModules 代表该 chunk
文件带来新的模块。

var moduleId, chunkId, i = 0, callbacks = [];
for(;i < chunkIds.length; i++) {
 chunkId = chunkIds[i];
 if(installedChunks[chunkId])
 callbacks.push.apply(callbacks, installedChunks[chunkId]);
 installedChunks[chunkId] = 0;
}
for(moduleId in moreModules) {
 if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
 modules[moduleId] = moreModules[moduleId];
 }
}
while(callbacks.length)
 callbacks.shift().call(null, __webpack_require__);
if(moreModules[0]) {
 installedModules[0] = 0;
 return __webpack_require__(0);
}

粗略说说 webpackJsonpCallback 做了怎样事,首先判断 chunkIds 在
installedChunks 里有没有回调函数函数未履行完,有的话则停放 callbacks
里,并且等下统一举办,并将 chunkIds 在 installedChunks 中全体置为 0,
然后将 moreModules 合并到 modules。

那中间唯有 modules[0] 是不稳定的,别的 modules
下标都以绝无仅有的,在卷入的时候 webpack 已经为它们统一号码,而 0
则为进口文件即 pageA,pageB 各有一个 module[0]。

接下来将 callbacks
执行并清空,保证了该模块加载早先前所以前置正视内容已经加载完结,最终判断
moreModules[0], 有值表达该文件为输入文件,则始于实践入口模块 0。

地点表达了一大堆,但是像 pageA 那种联合加载 manifest, vendor 以及 pageA
文件来说,每一次加载的时候 callbacks 都以为空的,因为它们在
installedChunks 中的值要嘛为 undefined(未加载), 要嘛为
0(已被加载)。installedChunks[chunkId] 的值永远为
false,所以在那种情状下 callbacks
里一向不会油但是生函数,若是单单是考虑那样的气象,下面的
webpackJsonpCallback 完全能够写成上边那样:

var moduleId, chunkId, i = 0, callbacks = [];
for(;i < chunkIds.length; i++) {
 chunkId = chunkIds[i];
 installedChunks[chunkId] = 0;
}
for(moduleId in moreModules) {
 if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
 modules[moduleId] = moreModules[moduleId];
 }
}
if(moreModules[0]) {
 installedModules[0] = 0;
 return __webpack_require__(0);
}

唯独考虑到异步加载 js 文件的时候(比如 pageB 异步加载 utilC
文件),就没那么粗略,大家先来看下 webpack 是怎么加载异步脚本的:

// 异步加载函数挂载在 __webpack_require__.e 上
__webpack_require__.e = function requireEnsure(chunkId, callback) {
 if(installedChunks[chunkId] === 0)
 return callback.call(null, __webpack_require__);

 if(installedChunks[chunkId] !== undefined) {
 installedChunks[chunkId].push(callback);
 } else {
 installedChunks[chunkId] = [callback];
 var head = document.getElementsByTagName('head')[0];
 var script = document.createElement('script');
 script.type = 'text/javascript';
 script.charset = 'utf-8';
 script.async = true;

 script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"pageA","1":"pageB","3":"vendor"}[chunkId]||chunkId) + "." + {"0":"e72ce7d4","1":"69f6bbe3","2":"9adbbaa0","3":"53fa02a7"}[chunkId] + ".js";
 head.appendChild(script);
 }
};

大概分为两种情景,(已经加载过,正在加载中以及从未加载过)

  1. 早就加载过该 chunk 文件,那就无须再重新加载该 chunk
    了,直接执行回调函数即可,能够掌握为要是页面有两种操作要求加载加载异步脚本,但是八个剧本都依靠于公私模块,那么第三回加载的时候发现以前率先次操作已经加载过了该
    chunk,则毫不再去获取异步脚本了,因为该公共模块已经被实践过了。
  2. 从没加载过,则动态地去插入 script 脚本去央求 js
    文件,那也就干什么取名 webpackJsonpCallback ,因为跟 jsonp
    的商量很类似,所以那种异步加载脚本在做脚本错误监察和控制时常常出现 Script
    error,具体原因能够查阅自身事先写的稿子:前端代码万分监察和控制实战
  3. 正在加载中表示该 chunk
    文件已经在加载中了,比如说点击按钮触发异步脚本,用户点太快了,连点一次就可能现身那种意况,此时将回调函数放入
    installedChunks。

大家经过 utilC 生成的 chunk 来拓展讲解:

webpackJsonp([2,4],{
 4: (function(module, exports) {
 module.exports = "util C";
 })
});

pageB 必要异步加载那个 chunk:

webpackJsonp([1,4],[
/* 0 */
 (function(module, exports, __webpack_require__) {
 const utilB = __webpack_require__(3);
 console.log(utilB);
 const utilC = () => __webpack_require__.e/* nsure */(2, function(require) {
  console.log(__webpack_require__(4))
 });
 utilC();
 })
]);

当 pageB 进行某种操作供给加载 utilC 时就会实施
__webpack_require__.e(2, callback) 2,代表要求加载的模块
chunkId(utilC),异步加载 utilC 并将 callback 添加到 installedChunks[2]
中,然后当 utilC 的 chunk 文件加载实现后,chunkIds 包涵 2,发现
installedChunks[2] 是个数组,里面还有以前还未执行的 callback 函数。

既然这样,那自个儿就将自作者要好带来的模块先放到 modules
中,然后再统一实施此前未进行完的 callbacks 函数,那里指的是存放在于
installedChunks[2] 中的回调函数
(恐怕存在四个),那相当于表明那里的先后顺序:

// 先将 moreModules 合并到 modules, 再去执行 callbacks, 不然之前未执行的 callback 依赖于新来的模块,你不放进 module 我岂不是得不到想要的模块
for(moduleId in moreModules) {
 if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
 modules[moduleId] = moreModules[moduleId];
 }
}
while(callbacks.length)
 callbacks.shift().call(null, __webpack_require__);

二.html文件中央处理须要

前端项目得以大概分成 单页面应用
多页面使用,现代化组件中的html文本重视用作走访入口文件,是<style>
样式标签和<script>本子标签的挂载点,打包中要求消除的主导难题包罗:

  • 性格化内容填充(例如页面标题,描述,关键词)
  • 余下空格删除(接二连三多少个空白字符的联结)
  • 代码压缩(多余空白字符的统一)
  • 剔除注释

4858美高梅 3

webpack2 哪些成功 tree shaking?

怎样是 tree shaking,即 webpack
在包装的经过中会将没用的代码进行铲除(dead code)。一般 dead code
具有一下的风味:

  1. 代码不会被实施,不可到达
  2. 代码执行的结果不会被用到
  3. 代码只会潜移默化死变量(只写不读)

是否很神奇,那么供给咋办才能使 tree shaking 生效呢?

率先,模块引入要基于 ES6 模块机制,不再行使 commonjs 规范,因为 es6
模块的重视关系是规定的,和运维时的地方毫无干系,能够开始展览保证的静态分析,然后去掉没用的代码。而
commonjs 的依赖性关系是要到运维时候才能明确下来的。

说不上,要求打开 UglifyJsPlugin 那些插件对代码实行压缩。

笔者们先写2个事例来表达:

// src/es6/pageA.js
import {
 utilA,
 funcA, // 引入 funcA 但未使用, 故 funcA 会被清除
} from './js/utilA';
import utilB from './js/utilB'; // 引入 utilB(函数) 未使用,会被清除
import classC from './js/utilC'; // 引入 classC(类) 未使用,不会被清除
console.log(utilA);

// src/es6/js/utilA.js
export const utilA = 'util A';
export function funcA() {
 console.log('func A');
}

// src/es6/js/utilB.js
export default function() {
 console.log('func B');
}
if(false) { // 被清除
 console.log('never use');
}
while(true) {}
console.log('never use');

// src/es6/js/utilC.js
const classC = function() {} // 类方法不会被清除
classC.prototype.saySomething = function() {
 console.log('class C');
}
export default classC;

4858美高梅,装进的布署也很简单:

const webpack = require('webpack');
const path = require('path')
module.exports = {
 entry: {
 pageA: path.resolve(__dirname, '../src/es6/pageA.js'),
 },
 output: {
 path: path.resolve(__dirname, '../dist'),
 filename: '[name].[chunkhash:8].js'
 },
 plugins: [
 new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  minChunks: Infinity,
 }),
 new webpack.optimize.UglifyJsPlugin({
  compress: {
  warnings: false
  }
 })
 ]
}

通过 npm run build:es6 对减少的文书实行辨析:

// dist/pageA.xxxx.js
webpackJsonp([0],[
 function(o, t, e) {
 'use strict';
 Object.defineProperty(t, '__esModule', { value: !0 });
 var n = e(1);
 e(2), e(3);
 console.log(n.a);
 },function(o, t, e) {
 'use strict';
 t.a = 'util A';
 },function(o, t, e) {
 'use strict';
 for (;;);
 console.log('never use');
 },
 function(o, t, e) {
 'use strict';
 const n = function() {};
 n.prototype.saySomething = function() {
  console.log('class C');
 };
 }
],[0]);

引入可是没用的变量,函数都会免去,未执行的代码也会被免去。不过类格局是不会被免去的。因为
webpack 不会区分不了是概念在 classC 的 prototype 依然其余 Array 的
prototype 的,比如 classC 写成上边那样:

const classC = function() {}
var a = 'class' + 'C';
var b;
if(a === 'Array') {
 b = a;
}else {
 b = 'classC';
}
b.prototype.saySomething = function() {
 console.log('class C');
}
export default classC;

webpack 不能担保 prototype 挂载的靶子是
classC,那种代码,静态分析是分析不了的,固然能静态分析代码,想要正确完全的剖析也比较劳累。所以
webpack 干脆不处理类情势,不对类方法进行 tree shaking。

越来越多的 tree shaking 的副功效能够查看: Tree shaking class
methods

webpack1 和 webpack2 在文件打包上有哪些分别?

经过自家对包裹文件的体察,从 webpack1 到 webpack2
在卷入文件上有上边那几个重点的更改:

首先,moduleId[0] 不再为输入实施函数做保留,所以说并非傻傻看到
moduleId[0] 就觉得是包裹文件的进口模块,取而代之的是
window[“webpackJsonp”] = function webpackJsonpCallback(chunkIds,
moreModules, executeModules) {} 传入了第多少个参数
executeModules,是个数组,如果参数存在则注明它是进口模块,然后就去执行该模块。

if(executeModules) {
 for(i=0; i < executeModules.length; i++) {
 result = __webpack_require__(__webpack_require__.s = executeModules[i]);
 }
}

说不上,webpack2 中会私下认可加载 OccurrenceOrderPlugin 那几个插件,即你绝不
plugins 中丰裕那些布局它也会暗中同意执行,那它有怎么着用场呢?主假使在
webpack1 中 moduleId 的不分明性导致的,在 webpack1 中 moduleId
取决于引入文件的依次,这就会招致这些 moduleId 恐怕会平常产生变化, 而
OccurrenceOrderPlugin
插件会按引入次数最多的模块实行排序,引入次数的模块的 moduleId
越小,比如说上面引用的 utilB 模块引用次数为 2(最多),所以它的 moduleId
为 0。

webpackJsonp([3],[
/* 0 */
 (function(module, exports) {
 module.exports = 'util B';
 })
]);

末尾说下在异步加载模块时, webpack2 是依照 Promise
的,所以说要是你要合营低版本浏览器,要求引入 Promise-polyfill
,此外为引入请求添加了错误处理。

__webpack_require__.e = function requireEnsure(chunkId) {
 var promise = new Promise(function(resolve, reject) {
 installedChunkData = installedChunks[chunkId] = [resolve, reject];
 });
 installedChunkData[2] = promise;
 // start chunk loading
 var head = document.getElementsByTagName('head')[0];
 var script = document.createElement('script');
 script.type = 'text/javascript';
 script.charset = 'utf-8';
 script.async = true;
 script.timeout = 120000;
 script.src = __webpack_require__.p + "" + chunkId + "." + {"0":"ae9c5f5f","1":"0ac69acb","2":"20651a9c","3":"0cdc6c84"}[chunkId] + ".js";
 var timeout = setTimeout(onScriptComplete, 120000);
 script.onerror = script.onload = onScriptComplete;
 function onScriptComplete() {
 // 防止内存泄漏
 script.onerror = script.onload = null;
 clearTimeout(timeout);
 var chunk = installedChunks[chunkId];
 if(chunk !== 0) {
  if(chunk) {
  chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
  }
  installedChunks[chunkId] = undefined;
 }
 };
 head.appendChild(script);
 return promise;
};

能够观望,原本基于回调函数的点子已经济体改为基于 Promise
做异步处理,其余添加了 onScriptComplete 用于做脚本加载失利处理。

在 webpack1
的时候,如若是因为网络原因当您加载脚本失利后,即便互联网恢复生机了,你再次开始展览某种操作须要同个
chunk 时候都会失效,主因是没戏以后没把 installedChunks[chunkId] =
undefined; 导致以往不会再对该 chunk 文件发起异步请求。

而在 webpack2 中,当脚本请求超时了(2min)大概加载战败,会将
installedChunks[chunkId] 清空,当下次再也请求该 chunk
文件会重新加载,升高了页面包车型客车容错性。

那几个是本人在包装文件中观望主要的差异,难免存有遗漏,借使您有更加多的见地,欢迎在评论区留言。

三.入口html文件的处理

一. Js模块化开发

javascript从而供给打包合并,是因为模块化开发的存在。开发阶段大家须要将js文本分别写在重重零星的文本中,方便调节和测试和修改,但万一就那样上线,那首页的http伸手数量将一向爆炸。同多个体系,别人2-三个请求就获得了急需的文书,而你的恐怕供给20-三贰13个,结果就不用多说了。

不过合并脚本可不是“把具备的零碎文件都拷贝到1个js文件里”这么就能化解的,不仅要缓解命名空间抵触的题材,还要求般配不一样的模块化方案,更别提依据模块之间复杂的注重关系来手动确定模块的加载顺序了,所以使用自动化学工业具来将开发阶段的js本子碎片举行统一和优化是特别有必不可少的。

webpack3 怎么办到 scope hoisting?

scope hoisting,顾名思义正是将模块的效能域提高,在 webpack
中不能够将全体全数的模块直接放在同三个成效域下,有以下多少个原因:

  1. 按需加载的模块
  2. 应用 commonjs 规范的模块
  3. 被多 entry 共享的模块

在 webpack3
中,这么些情况变化的模块不会议及展览开成效域进步,下面作者就举个例子来评释:

// src/hoist/utilA.js
export const utilA = 'util A';
export function funcA() {
 console.log('func A');
}

// src/hoist/utilB.js
export const utilB = 'util B';
export function funcB() {
 console.log('func B');
}

// src/hoist/utilC.js
export const utilC = 'util C';

// src/hoist/pageA.js
import { utilA, funcA } from './utilA';
console.log(utilA);
funcA();

// src/hoist/pageB.js
import { utilA } from './utilA';
import { utilB, funcB } from './utilB';

funcB();
import('./utilC').then(function(utilC) {
 console.log(utilC);
})

本条例子相比卓越,utilA 被 pageA 和 pageB 所共享,utilB 被 pageB
单独加载,utilC 被 pageB 异步加载。

想要 webpack3 生效,则要求在 plugins 中添加 ModuleConcatenationPlugin。

webpack 配置如下:

const webpack = require('webpack');
const path = require('path')
module.exports = {
 entry: {
 pageA: path.resolve(__dirname, '../src/hoist/pageA.js'),
 pageB: path.resolve(__dirname, '../src/hoist/pageB.js'),
 },
 output: {
 path: path.resolve(__dirname, '../dist'),
 filename: '[name].[chunkhash:8].js'
 },
 plugins: [
 new webpack.optimize.ModuleConcatenationPlugin(),
 new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: 2,
 }),
 new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  minChunks: Infinity,
 })
 ]
}

运行 npm run build:hoist 进行编写翻译,不难看下生成的 pageB 代码:

webpackJsonp([2],{
 2: (function(module, __webpack_exports__, __webpack_require__) {
 "use strict";
 var utilA = __webpack_require__(0);
 // CONCATENATED MODULE: ./src/hoist/utilB.js
 const utilB = 'util B';
 function funcB() {
  console.log('func B');
 }
 // CONCATENATED MODULE: ./src/hoist/pageB.js
 funcB();
 __webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 3)).then(function(utilC) {
  console.log(utilC);
 })
 })
},[2]);

因此代码分析,能够得出上面包车型的士定论:

  1. 因为我们安顿了共享模块抽离,所以 utilA
    被挤出为单身模块,故那部分剧情不会进展功用域提高。
  2. utilB 无牵无挂,被 pageB
    单独加载,所以那有些不会变动新的模块,而是径直效用域进步到 pageB
    中。
  3. utilC 被异步加载,必要抽离成单身模块,很鲜明不能够作用域升高。

webpack2 怎么样实现 tree shaking?

什么是 tree shaking,即 webpack
在包装的历程中会将没用的代码举行割除(dead code)。一般 dead code
具有一下的表征:

  1. 代码不会被实施,不可到达
  2. 代码执行的结果不会被用到
  3. 代码只会影响死变量(只写不读)

是或不是很神奇,那么须求如何是好才能使 tree shaking 生效呢?

首先,模块引入要依照 ES6 模块机制,不再选拔 commonjs 规范,因为 es6
模块的信赖关系是明确的,和周转时的情状非亲非故,可以举行保障的静态分析,然后去掉没用的代码。而
commonjs 的信赖性关系是要到运营时候才能分明下来的。

帮助,供给敞开 UglifyJsPlugin 这一个插件对代码实行压缩。

大家先写贰个例证来证实:

// src/es6/pageA.js
import {
 utilA,
 funcA, // 引入 funcA 但未使用, 故 funcA 会被清除
} from './js/utilA';
import utilB from './js/utilB'; // 引入 utilB(函数) 未使用,会被清除
import classC from './js/utilC'; // 引入 classC(类) 未使用,不会被清除
console.log(utilA);

// src/es6/js/utilA.js
export const utilA = 'util A';
export function funcA() {
 console.log('func A');
}

// src/es6/js/utilB.js
export default function() {
 console.log('func B');
}
if(false) { // 被清除
 console.log('never use');
}
while(true) {}
console.log('never use');

// src/es6/js/utilC.js
const classC = function() {} // 类方法不会被清除
classC.prototype.saySomething = function() {
 console.log('class C');
}
export default classC;

卷入的布局也很简短:

const webpack = require('webpack');
const path = require('path')
module.exports = {
 entry: {
 pageA: path.resolve(__dirname, '../src/es6/pageA.js'),
 },
 output: {
 path: path.resolve(__dirname, '../dist'),
 filename: '[name].[chunkhash:8].js'
 },
 plugins: [
 new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  minChunks: Infinity,
 }),
 new webpack.optimize.UglifyJsPlugin({
  compress: {
  warnings: false
  }
 })
 ]
}

经过 npm run build:es6 对减价扣的文书举办剖析:

// dist/pageA.xxxx.js
webpackJsonp([0],[
 function(o, t, e) {
 'use strict';
 Object.defineProperty(t, '__esModule', { value: !0 });
 var n = e(1);
 e(2), e(3);
 console.log(n.a);
 },function(o, t, e) {
 'use strict';
 t.a = 'util A';
 },function(o, t, e) {
 'use strict';
 for (;;);
 console.log('never use');
 },
 function(o, t, e) {
 'use strict';
 const n = function() {};
 n.prototype.saySomething = function() {
  console.log('class C');
 };
 }
],[0]);

引入不过对事情没有什么益处的变量,函数都会免去,未进行的代码也会被拔除。可是类措施是不会被排除的。因为
webpack 不会分别不了是概念在 classC 的 prototype 照旧别的 Array 的
prototype 的,比如 classC 写成上面那样:

const classC = function() {}
var a = 'class' + 'C';
var b;
if(a === 'Array') {
 b = a;
}else {
 b = 'classC';
}
b.prototype.saySomething = function() {
 console.log('class C');
}
export default classC;

webpack 不大概担保 prototype 挂载的靶子是
classC,那种代码,静态分析是分析不了的,固然能静态分析代码,想要正确完全的辨析也相比辛苦。所以
webpack 干脆不处理类格局,不对类方法开始展览 tree shaking。

越来越多的 tree shaking 的副功能能够查看: Tree shaking class methods

3.1 单页面应用打包

对于入口html文本的拍卖直接使用html-webpack-plugin插件来安装一定的配置参数即可,详细的配置参数能够参照其github地址:html-webpack-plugin项目地址,在此直接付出基本用法示例。

webpack.config.js配置:

4858美高梅 4

index.html模板文件(营造转变的进口页面是以此为模板的):

4858美高梅 5

包裹后生成的index.html:

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Document</title></head><body><div><p>tony stark</p><p>bruce banner </p></div><script type="text/javascript" src="main.boundle.js"></script></body></html>

二. Js文件的貌似打包须求

  • 代码编写翻译(TSES6代码的编写翻译)
  • 本子合并
  • 公物模块识别
  • 代码分割
  • 代码压缩混淆

结尾

好了,讲到那基本上就完了,明白地方的内容对前者模块化会有越多的体味,假使有怎么着写的非通常或许不完全的地点,还望补充表明,希望那篇小说能援救到你。

如上正是本文的全体内容,希望对大家的就学抱有补助,也期望大家多多援助脚本之家。

webpack3 如何做到 scope hoisting?

scope hoisting,顾名思义就是将模块的功用域升高,在 webpack
中不能够将全数全部的模块直接放在同2个效应域下,有以下多少个原因:

  1. 按需加载的模块
  2. 使用 commonjs 规范的模块
  3. 被多 entry 共享的模块

在 webpack3
中,那个情形变化的模块不会进展功效域升高,下边作者就举个例子来验证:

// src/hoist/utilA.js
export const utilA = 'util A';
export function funcA() {
 console.log('func A');
}

// src/hoist/utilB.js
export const utilB = 'util B';
export function funcB() {
 console.log('func B');
}

// src/hoist/utilC.js
export const utilC = 'util C';

// src/hoist/pageA.js
import { utilA, funcA } from './utilA';
console.log(utilA);
funcA();

// src/hoist/pageB.js
import { utilA } from './utilA';
import { utilB, funcB } from './utilB';

funcB();
import('./utilC').then(function(utilC) {
 console.log(utilC);
})

其一例子相比出色,utilA 被 pageA 和 pageB 所共享,utilB 被 pageB
单独加载,utilC 被 pageB 异步加载。

想要 webpack3 生效,则要求在 plugins 中添加 ModuleConcatenationPlugin。

webpack 配置如下:

const webpack = require('webpack');
const path = require('path')
module.exports = {
 entry: {
 pageA: path.resolve(__dirname, '../src/hoist/pageA.js'),
 pageB: path.resolve(__dirname, '../src/hoist/pageB.js'),
 },
 output: {
 path: path.resolve(__dirname, '../dist'),
 filename: '[name].[chunkhash:8].js'
 },
 plugins: [
 new webpack.optimize.ModuleConcatenationPlugin(),
 new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: 2,
 }),
 new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  minChunks: Infinity,
 })
 ]
}

运营 npm run build:hoist 实行编写翻译,简单看下生成的 pageB 代码:

webpackJsonp([2],{
 2: (function(module, __webpack_exports__, __webpack_require__) {
 "use strict";
 var utilA = __webpack_require__(0);
 // CONCATENATED MODULE: ./src/hoist/utilB.js
 const utilB = 'util B';
 function funcB() {
  console.log('func B');
 }
 // CONCATENATED MODULE: ./src/hoist/pageB.js
 funcB();
 __webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 3)).then(function(utilC) {
  console.log(utilC);
 })
 })
},[2]);

经过代码分析,能够汲取上边包车型大巴定论:

  1. 因为我们配备了共享模块抽离,所以 utilA
    被挤出为独立模块,故那有的内容不会开始展览作用域进步。
  2. utilB 无牵无挂,被 pageB
    单独加载,所以那有的不会转变新的模块,而是径直功用域进步到 pageB
    中。
  3. utilC 被异步加载,必要抽离成独立模块,很醒目不能够效能域升高。

3.2 多页面使用打包

一旦项目中有八个页面,那么打包的时候须要考虑三个为主难点:

  • 1.怎么样自动生成四个页面?
  • 2.万一引用中留存公共的模块,怎么着才能提取公共模块?

为了演示多页面使用打包的气象,大家来构建如下的一组示例项目及其注重关系:

4858美高梅 6

多页面使用的着力构造掌握起来并不复杂,能够将其当做是三个单页面应用的结合,在webpack中需求展开一些布置调整:

entry参数必要布置三个依靠进口文件:

entry:{
    "main":__dirname + "/src/indexController.js",
    "about":__dirname + "/src/aboutController.js",
    "list":__dirname + "/src/listController.js",
},

html文件则要求各自引用对应的入口文件并扭转对应的拜会入口:

    plugins:[
        //index.html
        new HtmlWebpackPlugin({
            title:'MainPage',
            template:'src/index.html',
            filename:'index.html',
            templateParameters:{
                param1:'tony stark',
                param2:'bruce banner'
            },
            chunks:['main'],
       }),
        //about.html
        new HtmlWebpackPlugin({
            title:'AboutPage',
            template:'src/about.html',
            filename:'about.html',
            templateParameters:{
                param1:'tony stark',
                param2:'bruce banner'
            },
            chunks:['about'],
       }),
       //list.html
       new HtmlWebpackPlugin({
            title:'ListPage',
            template:'src/list.html',
            filename:'list.html',
            templateParameters:{
                param1:'tony stark',
                param2:'bruce banner'
            },
            chunks:['list'],
       }),
    ],

能够看来在变更html文件时早已为其单独引用了chunks数组中钦定的模块,那使得对应的页面生成时只依靠投机要求的脚本。

1.关于集人体模型块提取

4858美高梅 7

上一小节化解了多页面使用的骨干打包的需求,从获得的打包后的模块中,很不难见到它存在重新打包的题材,eventbus.js以此公共库被indexController.jsaboutController.js中均被引述,但在差别的chunks中被重复打包,当公共部分的容量较大时,那样的措施分明是无法承受的。实际上分包难题并不是多页面使用中才存在的,而且是非凡复杂的,它不光要考虑公共模块自己的尺寸,模块之间的引用关系,还索要考虑共同引用和异步引用等等10分多的题材,小编尚未商量清楚。

webpack1-3的本子中选取commonsChunkPlugin插件来消除这几个标题,在4.0之上的版本中丢掉了原来艺术,改为运用optimization.splitChunksoptimization.runtimeChunk来化解优化chunk拆分的难点,关于双方的分别能够看《webpack4:连奏中的进化》那篇博文。

2. 零部件模板html文件的拍卖

在基于Angular的花色中可能你会要求处理此类难题。github上点赞较多的Angular-webpack-starter品类对于html文本的处理是一贯运用raw-loader用作文本文件处理,测度个中间将html文件中的内容作为模板字符串使用并在框架之中开始展览了加工。

急需专注的是,html-webpack-plugin插件是信赖于html-loader而工作的,当您显式使用/\.html$/用作规则来筛选文件时,同样会挑选到作为入口文件的html财富,从而造成争辨报错。在Angularjs1.X品种中可考虑动用ngTemplage-loader插件。

三. 使用webpack处理js文件

你恐怕感兴趣的小说:

  • 详解webpack
    多页面/入口帮衬&公共组件单独包装
  • Vue.js中用webpack合并打包三个零件并落实按需加载
  • webpack独立包装和缓存处理详解
  • 详解怎么样使用webpack打包Vue工程
  • 详解webpack分离css单独打包
  • webpack打包后直接访问页面图片路径错误的解决形式
  • Webpack达成按需打包Lodash的两种办法详解
  • 彻底化解 webpack
    打包文件体积过大难题
  • Webpack打包慢难题的两全解决措施

结尾

好了,讲到那基本上就完了,驾驭地点的内容对前者模块化会有越来越多的回味,倘若有哪些写的歇斯底里只怕不完整的地点,还望补充表达,希望这篇小说能帮助到你。

以上正是本文的全体内容,希望对大家的求学抱有扶助,也愿意咱们多多帮助帮客之家。

webpack
文件打包机制(小结),深切了然webpack 前言 近日在重拾 webpack
一些知识点,希望对前者模块化有愈来愈多的明亮,从前对 webpac…

四. 小结

本文使用的html文件是较为容易的,仅包括基本的竹签和品质,并未包括其他财富引用(样式,图片等),究竟webpack的组成部分太过混乱,去除干扰新闻有指向的就学更便于精晓。能源管理及定点将在一而再的章节演讲。

3.1 使用babel转换ES6+语法

babelES6语法的变换工具,对babel不打听的读者能够先读书《大前端的自行化学工业厂(3)——贝布el》一文进行精通,babelwebpack整合使用的点子也在内部做了介绍,此处仅提供基本配备:

webpack.config.js:

...  
module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
             loader: 'babel-loader'
          }
        ]
      }
    ]
  },
 ...

.babelrc:

{
    "presets":[
        ["env",{
            "targets":{
                "browsers":"last 2 versions"
            }
        }
        ]],
    "plugins": [
         "babel-plugin-transform-runtime" 
    ]
}

3.2 脚本合并

使用webpack对台本举办联合是至极便于的,终究模块管理文本合并那七个功效是webpack早期安顿的首要用途,直到涉及到含有和懒加载的话题时才会变得复杂。webpack选用起来很有益于,是因为达成了对各类分歧模块规范的合营处理,对前者开发者来说,掌握那种包容性达成的法子比上学怎样布署webpack更为首要。webpack暗中同意协助的是CommonJs正式,但与此同时为了扩展其利用情形,webpack在后续的版本迭代中也加入了对ES harmony等其他专业定义模块的合营处理,具体的处理方式将在下一章《webpack4.0各类击破(5)——
Module篇》
详细分析。

3.3 公共模块识别

webpack的出口的文书中能够看来如下的有的:

/******/    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;
/******/    }

上面的__webpack_require__( )方法便是webpack的模块加载器,很不难看到在那之中对于已加载的模块是有联合的installedModules对象来保管的,那样就防止了模块重复加载的标题。而集人体模型块一般也急需从bundle.js文件中提取出来,那事关到下一节的“代码分割”的内容。

3.4 代码分割

4858美高梅 8

1. 为啥要开始展览代码分割?

代码分割最大旨的天职是分手出第1方重视库,因为第①方库的始末也许很久都不会改变,所以用来标记变化的摘要哈希contentHash也很久不变,那也就代表大家能够采纳本地缓存来防止没有须求的再度打包,并使用浏览器缓存制止冗余的客户端加载。此外当项目揭穿新本鸡时,要是第壹方依赖的contentHash未曾转变,就能够动用客户端原来的缓存文件(通用的做法一般是给静态能源请求设置三个相当的大的max-age),提高访问速度。其它一些景色中,代码分割也得以提供对剧本在整个加载周期内的加载时机的控制能力。

2. 代码分割的选取意况

举个很常见的例子,比如您在做多少个数据可视化类型的网站,引用到了百度的Echarts作为第②方库来渲染图表,要是您将协调的代码和Echarts包装在协同生成1个main.bundle.js文件,那样的结果正是在一个网速欠佳的条件下开辟你的网站时,用户大概需求直面相当短日子的白屏,你火速就会想到将Echarts从主文件中脱离出去,让体积较小的主文件先在界面上渲染出部分卡通或是提醒音讯,然后再去加载Echarts,而分手出的Echarts也足以从进程更快的CDN节点获取,假设加载有些体量庞大的库,你也得以选用使用懒加载的方案,将脚本的下载时机延迟到用户真正使用相应的机能在此以前。那正是一种人工的代码分割。

从地方的事例整个的生命周期来看,大家将原本2遍就能够加载完的本子拆分为了三回,那活脱脱会加剧服务端的性子开支,毕竟建立TCP连接是一种开销非常的大的操作,但这么做却足以换成对渲染节奏的主宰和用户体验的晋升异步模块懒加载模块从微观上来讲实际上都属于代码分割的范畴。code splitting最极致的情景其实正是拆分成打包前的天生,也正是源码直接上线

3. 代码分割的实质

4858美高梅 9

代码分割的本质,正是在“源码直接上线”“打包为唯一的本子main.bundle.js”那两种极端方案之间寻找一种更符合实际场景的中间状态,用可接受的服务器品质压力加码来换取更好的用户体验。

4. 布局代码分割

code-splitting技术的配置和利用办法将在下一小节详细描述。

5. 更仔细的代码分割

感兴趣的读者能够参见来自google开发者社区的小说《Reduce JavaScript
Payloads with Code
Splitting》自行钻研。

3.5 代码混淆压缩

webpack4中曾经嵌入了UglifyJs插件,当打包格局参数mode设置为production时就会自动开启,当然这不是绝无仅有的选拔,babel的插件中也能提供代码压缩的拍卖,具体的意义和法则作者尚未深究,感兴趣的读者能够自动钻研。

四. 细说splitChunks技术

4.1 参数表达

webpack4废弃了CommonsChunkPlugin插件,使用optimization.splitChunksoptimization.runtimeChunk来代替,原因能够参照《webpack4:连奏中的进化》一文。关于runtimeChunk参数,有的小说就是提取出入口chunk中的runtime部分,形成三个单独的文件,由于那有个别不常变化,能够应用缓存。google开发者社区的博文是如此讲述的:

 The runtimeChunk option is
also specified to move webpack’s
runtime into
the vendors chunk to avoid duplication of it in our app code.

splitChunks中暗中同意的代码自动分割要求是上边那样的:

  • node_modules中的模块或别的被另行引用的模块

    正是说若是引用的模块来自node_modules,那么只要它被引用,那么满意别的标准化时就足以拓展机动分割。不然该模块要求被重新引用才继续判断任何标准。(对应的便是下文配置选项中的minChunks为1或2的场景)

  • 离别前模块最小体量下限(暗许30k,可修改)

    30k是法定给出的暗许数值,它是足以修改的,上一节中早已讲过,每1遍分包对应的都以服务端的属性花费的充实,所以必要求考虑富含的性价比。

  • 对此异步模块,生成的共用模块文件不可能压倒多少个(可修改)

    接触了懒加载模块的下载时,并发请求不能跨越5个,对于有个别理解过服务端技术的开发者来说,【高并发】【压力测试】如此的机要词应该不会素不相识。

  • 对此入口模块,抽离出的公共模块文件不可能超出二个(可修改)

    也正是说四个输入文件的最大交互请求默许不得跨越三个,原因同上。

4.2 参数配置

splitChunks的在webpack4.0上述版本中的用法是上边这样的:

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',//默认只作用于异步模块,为`all`时对所有模块生效,`initial`对同步模块有效
      minSize: 30000,//合并前模块文件的体积
      minChunks: 1,//最少被引用次数
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',//自动命名连接符
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          minChunks:1,//敲黑板
          priority: -10//优先级更高
        },
        default: {
          test: /[\\/]src[\\/]js[\\/]/
          minChunks: 2,//一般为非第三方公共模块
          priority: -20,
          reuseExistingChunk: true
        }
      },
      runtimeChunk:{
          name:'manifest'
      }
    }
  }

4.3 代码分割实例

注:实例中应用的demo及布局文件已放在附属类小部件中。

  • 单页面应用

    单页面应用唯有1个入口文件,splitChunks的最主要功能是将引用的第叁方库拆分出来。从上面包车型地铁含有结果就足以看出,node_modules中的第壹方引用被分别了出来,放在了vendors-main.[hash].js中。

4858美高梅 10

  • 多页面使用

    多页面使用的情状稍显复杂,以《webpack4:连奏中的进化》一文中的例子进行代码分割处理,源码的借助关系为:

    entryA.js: vue vuex component10k
    entryB.js: vue axios component10k
    entryC.js: vue vuex axios component10k
    

    通过代码分割后取得的包如下图所示:

4858美高梅 11


splitChunks提供了更纯粹的细分策略,但是犹如无法间接通过html-webpack-plugin布署参数来动态消除分割后代码的注入难点,因为含有名称是不明确的。这一个场景在应用chunks:'async'私下认可配置时是不设有的,因为异步模块的引用代码是不须求以<script>标签的样式注入html文件的。

chunks配备项设置为allinitial时,就会有标题,例如地点示例中,通过在html-webpack-plugin中配置excludeChunks能够去除pageabout那八个chunk,但是却力不从心提前排除vendors-about-page以此chunk,因为包装前不可能知道是还是不是会扭转那样2个chunk。这一个场所我并不曾找到现成的消除方案,对此情况有须求的读者可能能够经过动用html-webpack-plugin事件扩张来拍卖此类现象,也足以选用折中方案,正是首先次打包后记录下新变化的chunk名称,按需填写至html-webpack-pluginchunks配置项里。

### 4.4 结果分析

通过Bundle Buddy浅析工具或webpack-bundle-analyser插件就能够看看分包前后对于公共代码的抽取带来的熏陶(图片来自参考文献的博文):

4858美高梅 12

五. 参考及附件表明

【1】附加粤语件表明:

  • webpack.spa.config.js——单页面应用代码分割配置实例
  • main.js——单页面应用入口文件
  • webpack.multi.config.js——多页面使用代码分割配置实例
  • entryA.js,entryB.js,entryC.js——多页面使用的贰个输入

【2】参考文献: 《Reduce JavaScript Payloads with Code
Splitting》

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有