最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
当前位置: 首页 - 科技 - 知识百科 - 正文

详解webpack模块及webpack3新特性

来源:懂视网 责编:小采 时间:2020-11-27 20:03:40
文档

详解webpack模块及webpack3新特性

详解webpack模块及webpack3新特性:本文从简单的例子入手,从打包文件去分析以下三个问题:webpack打包文件是怎样的?如何做到兼容各大模块化方案的?webpack3带来的新特性又是什么?webpack是一个强大的模块打包工具,在处理依赖、模块上都很优秀,本文从bundle.js文件分析出发去探索了不同
推荐度:
导读详解webpack模块及webpack3新特性:本文从简单的例子入手,从打包文件去分析以下三个问题:webpack打包文件是怎样的?如何做到兼容各大模块化方案的?webpack3带来的新特性又是什么?webpack是一个强大的模块打包工具,在处理依赖、模块上都很优秀,本文从bundle.js文件分析出发去探索了不同

本文从简单的例子入手,从打包文件去分析以下三个问题:webpack打包文件是怎样的?如何做到兼容各大模块化方案的?webpack3带来的新特性又是什么?webpack是一个强大的模块打包工具,在处理依赖、模块上都很优秀,本文从bundle.js文件分析出发去探索了不同模块方案的加载机制,初步去理解webpack,并且对webpack3特性进行阐述。

一个简单的例子

webpack配置

 // webpack.config.js
module.exports = {
 entry: './src/index.js',
 output: {
 filename: 'bundle.js',
 path: path.resolve(__dirname, 'dist')
 },
};

简单的js文件

 // src/index.js
 console.log('hello world');

webpack打包后的代码

一看你就会想,我就一行代码,你给我打包那么多???(黑人问号)

// dist/bundle.js
 /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
console.log('hello world');
/***/ })
/******/ ]);

我们来分析一下这部分代码,先精简一下,其实整体就是一个自执行函数,然后传入一个模块数组

 (function(modules) { 
 //...
 })([function(module, exports) {
 //..
 }])

好了,传入模块数组做了什么(其实注释都很明显了,我只是大概翻译一下)

 /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache 缓存已经load过的模块
/******/ var installedModules = {};
/******/
/******/ // The require function 引用的函数
/******/ 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, //模块id
/******/ l: false, // 是否已经加载完毕
/******/ exports: {} // 对外暴露的内容
/******/ };
/******/
/******/ // Execute the module function 传入模块参数,并执行模块
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded 标记模块已经加载完毕
/******/ module.l = true;
/******/
/******/ // Return the exports of the module 返回模块暴露的内容
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__) 暴露模块数组
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache 暴露缓存数组
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports 为ES6 exports定义getter
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) { // 假如exports本身不含有name这个属性
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules 解决ES module和Common js module的冲突,ES则返回module['default']
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__ webpack配置下的公共路径
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports 最后执行entry模块并且返回它的暴露内容
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
console.log('hello world');
/***/ })
/******/ ]);

整体流程是怎样的呢

  • 传入module数组

  • 调用__webpack_require__(__webpack_require__.s = 0)

  • 构造module对象,放入缓存

    调用module,传入相应参数modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); (这里exports会被函数内部的东西修改)

    标记module对象已经加载完毕

    返回模块暴露的内容(注意到上面函数传入了module.exports,可以对引用进行修改)

  • 模块函数中传入module, module.exports, __webpack_require__

  • 执行过程中通过对上面三者的引用修改,完成变量暴露和引用

  • webpack模块机制是怎样的

    我们可以去官网看下webpack模块

    doc.webpack-china.org/concepts/mo…

    webpack 模块能够以各种方式表达它们的依赖关系,几个例子如下:

  • ES2015 import 语句

  • CommonJS require() 语句

  • AMD define 和 require 语句

  • css/sass/less 文件中的 @import 语句。

  • 样式(url(...))或 HTML 文件()中的图片链接(image url)

  • 强大的webpack模块可以兼容各种模块化方案,并且无侵入性(non-opinionated)

    我们可以再编写例子一探究竟

    CommonJS

    修改src/index.js

    var cj = require('./cj.js');
    console.log('hello world');
    cj();

    新增src/cj.js,保持前面例子其他不变

    // src/cj.js
    function a() {
     console.log("CommonJS");
    }
    module.exports = a;

    再次运行webpack

    /******/ (function(modules) { // webpackBootstrap
     //... 省略代码
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, exports, __webpack_require__) {
    let cj = __webpack_require__(1);
    console.log('hello world');
    cj();
    /***/ }),
    /* 1 */
    /***/ (function(module, exports) {
    function a() {
     console.log("CommonJS");
    }
    module.exports = a;
    /***/ })
    /******/ ]);

    我们可以看到模块数组多了个引入的文件,然后index.js模块函数多了个参数__webpack_require__,去引用文件(__webpack_require__在上一节有介绍),整体上就是依赖的模块修改了module.exports,然后主模块执行依赖模块,获取exports即可

    ES2015 import

    新增src/es.js

    // src/es.js
    export default function b() {
     console.log('ES Modules');
    }

    修改src/index.js

    // src/index.js
    import es from './es.js';
    console.log('hello world');
    es();
    webpack.config.js不变,执行webpack
    /******/ (function(modules) { // webpackBootstrap
    // ... 省略代码
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__es_js__ = __webpack_require__(1);
    console.log('hello world');
    Object(__WEBPACK_IMPORTED_MODULE_0__es_js__["a" /* default */])();
    /***/ }),
    /* 1 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    /* harmony export (immutable) */ __webpack_exports__["a"] = b;
    function b() {
     console.log('ES Modules');
    }
    /***/ })
    /******/ ]);

    我们可以看到它们都变成了严格模式,webpack自动采用的

    表现其实跟CommonJS相似,也是传入export然后修改,在主模块再require进来,

    我们可以看到这个

    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

    这个干嘛用的?其实就是标记当前的exports是es模块,还记得之前的__webpack_require__.n吗,我们再拿出来看看

    /******/ // getDefaultExport function for compatibility with non-harmony modules 解决ES module和Common js module的冲突,ES则返回module['default']
    /******/ __webpack_require__.n = function(module) {
    /******/ var getter = module && module.__esModule ?
    /******/ function getDefault() { return module['default']; } :
    /******/ function getModuleExports() { return module; };
    /******/ __webpack_require__.d(getter, 'a', getter);
    /******/ return getter;
    /******/ };

    为了避免跟非ES Modules冲突?冲突在哪里呢?

    其实这部分如果你看到babel转换ES Modules源码就知道了,为了兼容模块,会把ES Modules直接挂在exports.default上,然后加上__esModule属性,引入的时候判断一次是否是转换模块,是则引入module['default'],不是则引入module

    我们再多引入几个ES Modules看看效果

    // src/es.js
    export function es() {
     console.log('ES Modules');
    }
    export function esTwo() {
     console.log('ES Modules Two');
    }
    export function esThree() {
     console.log('ES Modules Three');
    }
    export function esFour() {
     console.log('ES Modules Four');
    }

    我们多引入esTwo和esFour,但是不使用esFour

    // src/index.js
    import { es, esTwo, esFour} from './es.js';
    console.log('hello world');
    es();
    esTwo();

    得出

    /******/ (function(modules) { // webpackBootstrap
    // ...
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__es_js__ = __webpack_require__(1);
    console.log('hello world');
    Object(__WEBPACK_IMPORTED_MODULE_0__es_js__["a" /* es */])();
    Object(__WEBPACK_IMPORTED_MODULE_0__es_js__["b" /* esTwo */])();
    /***/ }),
    /* 1 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    /* harmony export (immutable) */ __webpack_exports__["a"] = es;
    /* harmony export (immutable) */ __webpack_exports__["b"] = esTwo;
    /* unused harmony export esThree */
    /* unused harmony export esFour */
    function es() {
     console.log('ES Modules');
    }
    function esTwo() {
     console.log('ES Modules Two');
    }
    function esThree() {
     console.log('ES Modules Three');
    }
    function esFour() {
     console.log('ES Modules Four');
    }
    /***/ })
    /******/ ]);

    嗯嗯其实跟前面是一样的,举出这个例子重点在哪里呢,有没有注意到注释中

    /* unused harmony export esThree */
    /* unused harmony export esFour */

    esThree是我们没有引入的模块,esFour是我们引用但是没有使用的模块,webpack均对它们做了unused的标记,其实这个如果你使用了webpack插件uglify,通过标记,就会把esThree和esFour这两个未使用的代码消除(其实它就是tree-shaking)

    AMD

    我们再来看看webpack怎么支持AMD

    新增src/amd.js

    // src/amd.js
    define([
    ],function(){
     return {
     amd:function(){
     console.log('AMD');
     }
     };
    });

    修改index.js

    // src/index.js
    define([
     './amd.js'
    ],function(amdModule){
     amdModule.amd();
    });

    得到

    /******/ (function(modules) { // webpackBootstrap
    // ... 省略代码
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, exports, __webpack_require__) {
    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
     __webpack_require__(1)
    ], __WEBPACK_AMD_DEFINE_RESULT__ = function(amdModule){
     amdModule.amd();
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
     __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
    /***/ }),
    /* 1 */
    /***/ (function(module, exports, __webpack_require__) {
    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
    ], __WEBPACK_AMD_DEFINE_RESULT__ = function(){
     return {
     amd:function(){
     console.log('AMD');
     }
     };
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
     __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
    /***/ })
    /******/ ]);

    先看amd.js整理一下代码

    function(module, exports, __webpack_require__) {
     var __WEBPACK_AMD_DEFINE_ARRAY__,
     __WEBPACK_AMD_DEFINE_RESULT__;
     !(
     __WEBPACK_AMD_DEFINE_ARRAY__ = [],
     __WEBPACK_AMD_DEFINE_RESULT__ = function() {
     return {
     amd: function() {
     console.log('AMD');
     }
     };
     }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
     __WEBPACK_AMD_DEFINE_RESULT__ !== undefined &&
     (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)
     );
    })

    简单来讲收集define Array然后置入返回函数,根据参数获取依赖

    apply对数组拆解成一个一个参数

    再看index.js模块部分

    function(module, exports, __webpack_require__) {
     var __WEBPACK_AMD_DEFINE_ARRAY__,
     __WEBPACK_AMD_DEFINE_RESULT__;
     !(
     __WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(1)],
     __WEBPACK_AMD_DEFINE_RESULT__ = function(amdModule) {
     amdModule.amd();
     }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
     __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && 
     (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)
     );
    }

    其实就是引入了amd.js暴露的{amd:[Function: amd]}

    css/image?

    css和image也可以成为webpack的模块,这是令人震惊的,这就不能通过普通的hack commonjs或者函数调用简单去调用了,这就是anything to JS,它就需要借助webpack loader去实现了

    像css就是转换成一段js代码,通过处理,调用时就是可以用js将这段css插入到style中,image也类似,这部分就不详细阐述了,有兴趣的读者可以深入去研究

    webpack3新特性

    我们可以再顺便看下webpack3新特性的表现

    具体可以看这里medium.com/webpack/web…

    Scope Hoisting

    我们可以发现模块数组是一个一个独立的函数然后闭包引用webpack主函数的相应内容,每个模块都是独立的,然后带来的结果是在浏览器中执行速度变慢,然后webpack3学习了Closure Compiler和RollupJS这两个工具,连接所有闭包到一个闭包里,放入一个函数,让执行速度更快,并且整体代码体积也会有所缩小

    我们可以实际看一下效果(要注意的是这个特性只支持ES Modules,是不支持CommonJs和AMD的)

    使用上面的例子,配置webpack.config.js,增加new webpack.optimize.ModuleConcatenationPlugin()

    const path = require('path');
    const webpack = require('webpack');
    module.exports = {
     entry: './src/index.js',
     output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist')
     },
     module: {
     },
     plugins: [
     new webpack.optimize.ModuleConcatenationPlugin(),
     ]
    };

    打包

    /******/ (function(modules) { // webpackBootstrap
    // ... 省略代码
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    // CONCATENATED MODULE: ./src/es.js
    function es() {
     console.log('ES Modules');
    }
    function esTwo() {
     console.log('ES Modules Two');
    }
    function esThree() {
     console.log('ES Modules Three');
    }
    function esFour() {
     console.log('ES Modules Four');
    }
    // CONCATENATED MODULE: ./src/index.js
    // src/index.js
    console.log('hello world');
    es();
    /***/ })
    /******/ ]);

    我们可以惊喜的发现没有什么require了,它们拼接成了一个函数,good!

    声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文档

    详解webpack模块及webpack3新特性

    详解webpack模块及webpack3新特性:本文从简单的例子入手,从打包文件去分析以下三个问题:webpack打包文件是怎样的?如何做到兼容各大模块化方案的?webpack3带来的新特性又是什么?webpack是一个强大的模块打包工具,在处理依赖、模块上都很优秀,本文从bundle.js文件分析出发去探索了不同
    推荐度:
    标签: 模块 新特性 webp
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top