您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息
免费发信息
三六零分类信息网 > 西双版纳分类信息网,免费分类信息发布

详解webpack模块及webpack3新特性

2024/3/7 6:12:46发布21次查看
本文从简单的例子入手,从打包文件去分析以下三个问题: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!
以上就是详解webpack模块及webpack3新特性的详细内容。
西双版纳分类信息网,免费分类信息发布

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录