详解webpack模块及webpack3新特性

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

一个简单的例子

webpack配置

 // webpack.config.jsmodule.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.jsfunction 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.jsexport default function b() { console.log('ES Modules');}

登录后复制

修改src/index.js

// src/index.jsimport 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.jsexport 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.jsimport { 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.jsdefine([],function(){ return {  amd:function(){   console.log('AMD');  } };});

登录后复制

修改index.js

// src/index.jsdefine([ './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.jsfunction 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.jsconsole.log('hello world');es();/***/ })/******/ ]);

登录后复制

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

以上就是详解webpack模块及webpack3新特性的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2786696.html

(0)
上一篇 2025年3月8日 17:52:35
下一篇 2025年3月7日 07:07:15

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • web前端页面生成exe可执行文件实例

    在 html5的崛起、javascript要一统天下之际,有一个名为【跨平台】的技术越来越火。为什么会这么火?因为软件开发者只需一次编写程序,即可在 windows、linux、mac、ios、android 等平台运行,大大降低了程序员的…

    2025年3月8日
    200
  • Vue+webpack基础配置分享

    最近在学习webpack,跟着课程一个单页面应用,在这里记录一下。这个部分主要讲了如何配置webpack的环境,以及webpack dev的配置。记录比较粗略,后续会更新。本文主要和大家介绍了vue+webpack项目基础配置教程,需要的朋…

    2025年3月8日 编程技术
    200
  • webpack无法通过IP地址访问localhost的问题

    1、问题描述: 今天用 webpack (v1.14.0)配置本地服务,为了能在移动端预览开发效果,需要通过 IP 地址生存二维码然后手机扫描访问。却发现不能通过 ip 访问 localhost。 2、解决方案: 经多方查询,在 npm r…

    编程技术 2025年3月8日
    200
  • webpack-dev-server远程访问配置教程

    本文主要为大家分享一篇webpack-dev-server远程访问配置方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧,希望能帮助到大家。 下面的package.json文件中配置的webpack-dev-server为…

    编程技术 2025年3月8日
    200
  • react、webpack、跨域代理多页面

    初学react,利用webpack进行工程化开发管理,遇到一些问题,如多页面处理,跨域代理的设置,如何同时引入使用jquery。第一次试水,简单写了一个表格组件。 先照着react官网提供的教程使用create-react-app创建rea…

    编程技术 2025年3月8日
    200
  • webpack的优化策略

    在我们构建单页面应用(vue, react)或封装插件时,很大机会用到webpack,这个javascript的打包工具.但项目代码日益增多时,webpack一些简单的配置会暴露种种弊端(打包时间长,代码大).以下是结合自身开发和学习web…

    编程技术 2025年3月8日
    200
  • react-native WebView 返回处理方法

    项目中有些页面内容是变更比较频繁的,这些页面我们会考虑用 网页 来解决。 在RN项目中提供一个公用的Web页,如果是网页内容,就跳转到这个界面展示。 此时会有一个问题是,网页会有一级页面,二级页面,这就会设计到导航栏返回键的处理(以及在An…

    2025年3月8日 编程技术
    200
  • web端实现后退强制刷新功能代码

    本文主要和大家介绍了微信web端后退强制刷新功能的实现代码,需要的朋友可以参考下,希望能帮助到大家。 具体代码如下所示: //生成uuid var uuidChars = “0123456789ABCDEFGHIJKLMNOPQRSTUVW…

    编程技术 2025年3月8日
    200
  • JS和WebService大文件上传代码分享

    在编写前端的过程中,难免会遇到文件上传的问题,当用户要上传较大的文件是,会被服务器端限制,阻止其上传,在asp.net中,调整服务器接受文件的大小的配置方法如下:  在ASP中配置Web.config文件的httpRuntime: 登录后复…

    编程技术 2025年3月8日
    200
  • web.xml的配置

    这次给大家带来web.xml的配置,web.xml配置注意事项有哪些,下面就是实战案例,一起来看一下。 nbsp;web-app PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application …

    编程技术 2025年3月8日
    200

发表回复

登录后才能评论