金沙澳门官网网址:深深领悟,0依次击破

作者: 前端知识  发布:2019-12-23

目录

前言

  • 一. 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 打包后的文件进行查阅,对其如何打包 JS 文件有了更深的理解,希望通过这篇文章,能够帮助读者你理解:

webpack作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高。本系列是笔者自己的学习记录,比较基础,希望通过问题 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习webpack工具中相应的处理办法。(本篇中的参数配置及使用方式均基于webpack4.0版本

本篇摘要:

本篇主要介绍基于webpack4.0splitChunks分包技术。

webpack 单文件如何进行打包? webpack 多文件如何进行代码切割? webpack1 和 webpack2 在文件打包上有什么区别? webpack2 如何做到 tree shaking? webpack3 如何做到 scope hoisting?

金沙澳门官网网址 1

本文所有示例代码全部放在我的 Github 上,看兴趣的可以看看:

金沙澳门官网网址 2

git clone https://github.com/happylindz/blog.gitcd blog/code/webpackBundleAnalysisnpm install

一. Js模块化开发

javascript之所以需要打包合并,是因为模块化开发的存在。开发阶段我们需要将js文件分开写在很多零碎的文件中,方便调试和修改,但如果就这样上线,那首页的http请求数量将直接爆炸。同一个项目,别人2-3个请求就拿到了需要的文件,而你的可能需要20-30个,结果就不用多说了。

但是合并脚本可不是“把所有的碎片文件都拷贝到一个js文件里”这样就能解决的,不仅要解决命名空间冲突的问题,还需要兼容不同的模块化方案,更别提根据模块之间复杂的依赖关系来手动确定模块的加载顺序了,所以利用自动化工具来将开发阶段的js脚本碎片进行合并和优化是非常有必要的。

webpack 单文件如何打包?

二. Js文件的一般打包需求

  • 代码编译(TSES6代码的编译)
  • 脚本合并
  • 公共模块识别
  • 代码分割
  • 代码压缩混淆

首先现在 webpack 作为当前主流的前端模块化工具,在 webpack 刚开始流行的时候,我们经常通过 webpack 将所有处理文件全部打包成一个 bundle 文件, 先通过一个简单的例子来看:

三. 使用webpack处理js文件

// src/single/index.jsvar index2 = require;var util = require;console.log;console.log;// src/single/index2.jsvar util = require;console.log;module.exports = "index 2";// src/single/util.jsmodule.exports = "Hello World";// 通过 config/webpack.config.single.js 打包const webpack = require;const path = requiremodule.exports = { entry: { index: [path.resolve(__dirname, '../src/single/index.js')], }, output: { path: path.resolve, filename: '[name].[chunkhash:8].js' },}

3.1 使用babel转换ES6 语法

babelES6语法的转换工具,对babel不了解的读者可以先阅读《大前端的自动化工厂(3)——Babel》一文进行了解,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" 
    ]
}

通过 npm run build:single 可看到打包效果,打包内容大致如下:

3.2 脚本合并

使用webpack对脚本进行合并是非常方便的,毕竟模块管理文件合并这两个功能是webpack最初设计的主要用途,直到涉及到分包和懒加载的话题时才会变得复杂。webpack使用起来很方便,是因为实现了对各种不同模块规范的兼容处理,对前端开发者来说,理解这种兼容性实现的方式比学习如何配置webpack更为重要。webpack默认支持的是CommonJs规范,但同时为了扩展其使用场景,webpack在后续的版本迭代中也加入了对ES harmony等其他规范定义模块的兼容处理,具体的处理方式将在下一章《webpack4.0各个击破(5)—— Module篇》详细分析。

// dist/index.xxxx.js { // 已经加载过的模块 var installedModules = {}; // 模块加载函数 function __webpack_require__ { 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__; module.exports = "index 2";}),/* 1 */(function { module.exports = "Hello World";}),/* 2 */(function(module, exports, __webpack_require__) { var index2 = __webpack_require__; index2 = __webpack_require__; var util = __webpack_require__; console.log; console.log,/* 3 */(function(module, exports, __webpack_require__) { module.exports = __webpack_require__;

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 代码分割

金沙澳门官网网址 3

1. 为什么要进行代码分割?

代码分割最基本的任务是分离出第三方依赖库,因为第三方库的内容可能很久都不会变动,所以用来标记变化的摘要哈希contentHash也很久不变,这也就意味着我们可以利用本地缓存来避免没有必要的重复打包,并利用浏览器缓存避免冗余的客户端加载。另外当项目发布新版本时,如果第三方依赖的contentHash没有变化,就可以使用客户端原来的缓存文件(通用的做法一般是给静态资源请求设置一个很大的max-age),提升访问速度。另外一些场景中,代码分割也可以提供对脚本在整个加载周期内的加载时机的控制能力。

2. 代码分割的使用场景

举个很常见的例子,比如你在做一个数据可视化类型的网站,引用到了百度的Echarts作为第三方库来渲染图表,如果你将自己的代码和Echarts打包在一起生成一个main.bundle.js文件,这样的结果就是在一个网速欠佳的环境下打开你的网站时,用户可能需要面对很长时间的白屏,你很快就会想到将Echarts从主文件中剥离出来,让体积较小的主文件先在界面上渲染出一些动画或是提示信息,然后再去加载Echarts,而分离出的Echarts也可以从速度更快的CDN节点获取,如果加载某个体积庞大的库,你也可以选择使用懒加载的方案,将脚本的下载时机延迟到用户真正使用对应的功能之前。这就是一种人工的代码分割。

从上面的例子整个的生命周期来看,我们将原本一次就可以加载完的脚本拆分为了两次,这无疑会加重服务端的性能开销,毕竟建立TCP连接是一种开销很大的操作,但这样做却可以换来对渲染节奏的控制和用户体验的提升异步模块懒加载模块从宏观上来讲实际上都属于代码分割的范畴。code splitting最极端的状况其实就是拆分成打包前的原貌,也就是源码直接上线

3. 代码分割的本质

金沙澳门官网网址 4

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

4. 配置代码分割

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

5. 更细致的代码分割

感兴趣的读者可以参考来自google开发者社区的文章《Reduce JavaScript Payloads with Code Splitting》自行研究。

首先 webpack 将所有模块包裹于一个函数中,并传入默认参数,这里有三个文件再加上一个入口模块一共四个模块,将它们放入一个数组中,取名为 modules,并通过数组的下标来作为 moduleId。 将 modules 传入一个自执行函数中,自执行函数中包含一个 installedModules 已经加载过的模块和一个模块加载函数,最后加载入口模块并返回。 __webpack_require__ 模块加载,先判断 installedModules 是否已加载,加载过了就直接返回 exports 数据,没有加载过该模块就通过 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__) 执行模块并且将 module.exports 给返回。

3.5 代码混淆压缩

webpack4中已经内置了UglifyJs插件,当打包模式参数mode设置为production时就会自动开启,当然这不是唯一的选择,babel的插件中也能提供代码压缩的处理,具体的效果和原理笔者尚未深究,感兴趣的读者可以自行研究。

很简单是不是,有些点需要注意的是:

四. 细说splitChunks技术

每个模块 webpack 只会加载一次,所以重复加载的模块只会执行一次,加载过的模块会放到 installedModules,下次需要需要该模块的值就直接从里面拿了。 模块的 id 直接通过数组下标去一一对应的,这样能保证简单且唯一,通过其它方式比如文件名或文件路径的方式就比较麻烦,因为文件名可能出现重名,不唯一,文件路径则会增大文件体积,并且将路径暴露给前端,不够安全。 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__) 保证了模块加载时 this 的指向 module.exports 并且传入默认参数,很简单,不过多解释。webpack 多文件如何进行代码切割?

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是官方给出的默认数值,它是可以修改的,上一节中已经讲过,每一次分包对应的都是服务端的性能开销的增加,所以必须要考虑分包的性价比。

  • 对于异步模块,生成的公共模块文件不能超出5个(可修改)

    触发了懒加载模块的下载时,并发请求不能超过5个,对于稍微了解过服务端技术的开发者来说,【高并发】【压力测试】这样的关键词应该不会陌生。

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

    也就是说一个入口文件的最大并行请求默认不得超过3个,原因同上。

webpack 单文件打包的方式应付一些简单场景就足够了,但是我们在开发一些复杂的应用,如果没有对代码进行切割,将第三方库和业务代码全部打包在一起,就会导致用户访问页面速度很慢,不能有效利用缓存,你的老板可能就要找你谈话了。

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'
      }
    }
  }

那么 webpack 多文件入口如何进行代码切割,让我先写一个简单的例子:

4.3 代码分割实例

注:实例中使用的demo及配置文件已放在附件中。

  • 单页面应用

    单页面应用只有一个入口文件,splitChunks的主要作用是将引用的第三方库拆分出来。从下面的分包结果就可以看出,node_modules中的第三方引用被分离了出来,放在了vendors-main.[hash].js中。

金沙澳门官网网址 5

  • 多页面应用

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

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

    经过代码分割后得到的包如下图所示:

金沙澳门官网网址 6

splitChunks提供了更精确的分割策略,但是似乎无法直接通过html-webpack-plugin配置参数来动态解决分割后代码的注入问题,因为分包名称是不确定的。这个场景在使用chunks:'async'默认配置时是不存在的,因为异步模块的引用代码是不需要以<script>标签的形式注入html文件的。

chunks配置项设置为allinitial时,就会有问题,例如上面示例中,通过在html-webpack-plugin中配置excludeChunks可以去除pageabout这两个chunk,但是却无法提前排除vendors-about-page这个chunk,因为打包前无法知道是否会生成这样一个chunk。这个场景笔者并没有找到现成的解决方案,对此场景有需求的读者也许可以通过使用html-webpack-plugin事件扩展来处理此类场景,也可以使用折中方案,就是第一次打包后记录下新生成的chunk名称,按需填写至html-webpack-pluginchunks配置项里。

### 4.4 结果分析

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

金沙澳门官网网址 7

// src/multiple/pageA.jsconst utilA = require;const utilB = require;console.log;console.log;// src/multiple/pageB.jsconst utilB = require;console.log;// 异步加载文件,类似于 import => require.ensure(['./js/utilC'], function { console.log)});utilC();// src/multiple/js/utilA.js 可类比于公共库,如 jQuerymodule.exports = "util A";// src/multiple/js/utilB.jsmodule.exports = 'util B';// src/multiple/js/utilC.jsmodule.exports = "util C";

五. 参考及附件说明

【1】附加中文件说明:

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

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

这里我们定义了两个入口 pageA 和 pageB 和三个库 util,我们希望代码切割做到:

因为两入口都是用到了 utilB,我们希望把它抽离成单独文件,并且当用户访问 pageA 和 pageB 的时候都能去加载 utilB 这个公共模块,而不是存在于各自的入口文件中。 pageB 中 utilC 不是页面一开始加载时候就需要的内容,假如 utilC 很大,我们不希望页面加载时就直接加载 utilC,而是当用户达到某种条件才去异步加载 utilC,这时候我们需要将 utilC 抽离成单独文件,当用户需要的时候再去加载该文件。

那么 webpack 需要怎么配置呢?

// 通过 config/webpack.config.multiple.js 打包const webpack = require;const path = requiremodule.exports = { entry: { pageA: [path.resolve(__dirname, '../src/multiple/pageA.js')], pageB: path.resolve(__dirname, '../src/multiple/pageB.js'), }, output: { path: path.resolve, 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,})

这段代码的含义是,在这些入口文件中,找到那些引用两次的模块,帮我抽离成一个叫 vendor 文件,此时那部分初始化工作的代码会被抽离到 vendor 文件中。

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

这段代码的含义是在 vendor 文件中帮我把初始化代码抽离到 mainifest 文件中,此时 vendor 文件中就只剩下 utilB 这个模块了。你可能会好奇为什么要这么做?

因为这样可以给 vendor 生成稳定的 hash 值,每次修改业务代码,这段初始化时代码就会发生变化,那么如果将这段初始化代码放在 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 { 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.shift().call(null, __webpack_require__); if { installedModules[0] = 0; return __webpack_require__; } }; var installedModules = {}; var installedChunks = { 4:0 }; function __webpack_require__ { // 和单文件一致 } __webpack_require__.e = function requireEnsure { if(installedChunks[chunkId] === 0) return callback.call(null, __webpack_require__); if(installedChunks[chunkId] !== undefined) { installedChunks[chunkId].push; } else { installedChunks[chunkId] = [callback]; var head = document.getElementsByTagName[0]; var script = document.createElement; 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;

与单文件内容一致,定义了一个自执行函数,因为它不包含任何模块,所以传入一个空数组。除了定义了 __webpack_require__ ,还另外定义了两个函数用来进行加载模块。

首先讲解代码前需要理解两个概念,分别是 module 和 chunk

chunk 代表生成后 js 文件,一个 chunkId 对应一个打包好的 js 文件,从这段代码可以看出,manifest 的 chunkId 为 4,并且从代码中还可以看到:0-3 分别对应 pageA, pageB, 异步 utilC, vendor 公共模块文件,这也就是我们为什么不能将这段代码放在 vendor 的原因,因为文件的 hash 值会变。内容变了,vendor 生成的 hash 值也就变了。 module 对应着模块,可以简单理解为打包前每个 js 文件对应一个模块,也就是之前 __webpack_require__ 加载的模块,同样的使用数组下标作为 moduleId 且是唯一不重复的。

那么为什么要区分 chunk 和 module 呢?

首先使用 installedChunks 来保存每个 chunkId 是否被加载过,如果被加载过,则说明该 chunk 中所包含的模块已经被放到了 modules 中,注意是 modules 而不是 installedModules。我们先来简单看一下 vendor chunk 打包出来的内容。

// vendor.xxxx.jswebpackJsonp([3,4],{ 3: (function { 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.shift().call(null, __webpack_require__);if { installedModules[0] = 0; return __webpack_require__;}

简单说说 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。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 { installedModules[0] = 0; return __webpack_require__;}

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

// 异步加载函数挂载在 __webpack_require__.e 上__webpack_require__.e = function requireEnsure { if(installedChunks[chunkId] === 0) return callback.call(null, __webpack_require__); if(installedChunks[chunkId] !== undefined) { installedChunks[chunkId].push; } else { installedChunks[chunkId] = [callback]; var head = document.getElementsByTagName[0]; var script = document.createElement; 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; }};

大致分为三种情况,

本文由金沙澳门官网发布于前端知识,转载请注明出处:金沙澳门官网网址:深深领悟,0依次击破

关键词: 金沙澳门官网

上一篇:报表标签的运用,表格标签基本使用
下一篇:没有了