广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >webpack4.x CommonJS模块化的示例分析
  • 513
分享到

webpack4.x CommonJS模块化的示例分析

2024-04-02 19:04:59 513人浏览 安东尼
摘要

这篇文章主要为大家展示了“webpack4.x Commonjs模块化的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“WEBpack4.x Common

这篇文章主要为大家展示了“webpack4.x Commonjs模块化的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“WEBpack4.x CommonJS模块化的示例分析”这篇文章吧。

先看下webpack官方文档中对模块的描述:

在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。

每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。 精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。

webpack 的核心概念之一就是一切皆模块,webpack 在项目中的作用就是,分析项目的结构,找到 javascript 模块以及其他一些浏览器不能直接运行的拓展语言(less,sCSStypescript),并将其打包为合适的格式以供浏览器使用,它从一个项目的主文件开始,根据引用路径,找到所有其所依赖的文件,同时将这些文件进行处理(各种loader来解析,编译处理浏览器不能直接使用的文件),然后打包为一个或者多个浏览器可识别的JavaScript文件。

本文不会详细描述 webpack 的构建流程,毕竟官网已经说得比较详细了,这里主要是分析下 webpack 打包后的文件,将文件打包成什么样子,又是如何使用模块的。webpack 最早支持的代码模块化方式是 CommonJS,后面慢慢支持了 es6、AMD 等,不论使用的是哪种方式,webpack 都可以对其进行解析和打包,本文例子中使用的是 CommonJS 规范,更多规范介绍可查看官方文档。

例子

为方便后面的说明,首先创建一个项目,也就是先建立一个文件夹 webpack-test(名字自拟),然后在里面新建一个 package.JSON 文件,用来做 npm 的说明,在 webpack-test 文件夹中使用命令:

npm init

执行命令后会询问一些问题,一路回车即可。然后安装下 webpack 的依赖包,如下命令:

npm install --save-dev webpack

再新建几个文件:

1、在项目根目录下新建文件夹 app 用来存放业务代码、文件夹 public 存放打包后的文件;
2、在app中新建入口文件 main.js;
3、在app中新建功能模块 hello.js,bye.js,to.js;
4、在项目根目录下,建立 index.html 文件;

然后依次来给这几个文件分别填写以下内容:

// webpack-test/app/hello.js
const to = require('./to.js');
module.exports = function() {
 var hello = document.createElement('div');
 hello.textContent = "Say Hello to " + to.name;
 return hello;
};
// webpack-test/app/bye.js
const to = require('./to.js');
module.exports = function() {
 var bye = document.createElement('div');
 bye.textContent = "Say Bye to " + to.name;
 return bye;
};
// webpack-test/app/to.js
module.exports = {name: "小明"};
// webpack-test/app/main.js
const hello = require('./hello.js');
const bye = require('./bye.js');

document.querySelector("#root").appendChild(hello()).appendChild(bye());;
// webpack-test/index.html
<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>Webpack Test Project</title>
 </head>
 <body>
  <div id='root'></div>
  // bundle.js 文件就是一会儿我们要打包app中的文件后生成的结果文件
  <script src="public/bundle.js"></script>
 </body>
</html>

业务模块 hello.js 和 bye.js 做了各自的操作,同时引用了共同的文件 to.js;主文件 main.js 中引用并执行了模块 hello.js 和 bye.js;index.html 文件引入了 main.js 打包后的最终文件 bundle.js。

打包

接下来进行打包操作,先确保 webpack 是全局安装的,否则执行时需要指定 webpack 的路径,比如在 4.0 以下版本中使用 node_modules/.bin/webpack ./app/main.js ./public/bundle.js;
如果你使用的是 webpack4.0+ 的话,使用 webpack ./app/main.js ./public/bundle.js命令,也许会报以下的错误:

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/

ERROR in multi ./app/main.js ./public/bundle.js
Module not found: Error: Can't resolve './public/bundle.js' in '/Users/zhaohaipeng/soyoung-project/webpack-test'
 @ multi ./app/main.js ./public/bundle.js main[1]

webpack4.0+之后,针对第一个报错,需要指定环境 --mode development;第二个报错,是因为我们没有使用配置文件的方式打包,而是直接使用的命令指定的打包输出位置,所以需要声明输出文件,综上,正确的命令如下:

webpack app/main.js --output public/bundle.js --mode development

执行结果:

➜  webpack-test webpack app/main.js --output public/bundle.js --mode development
Hash: a4e2f9ecc51b64891624
Version: webpack 4.25.1
Time: 90ms
Built at: 2018-11-08 17:11:01
    Asset      Size  Chunks             Chunk Names
bundle.js  5.16 KiB    main  [emitted]  main
Entrypoint main = bundle.js
[./app/bye.js] 165 bytes {main} [built]
[./app/hello.js] 173 bytes {main} [built]
[./app/main.js] 144 bytes {main} [built]
[./app/to.js] 30 bytes {main} [built]
➜  webpack-test

浏览器打开 index.html 文件,即可看到结果

Say Hello to 小明
Say Bye to 小明

但是 webpack 作为一个能简化我们开发难度和使用便捷的工具,显然像上面那样通过敲很多命令来打包,并不方便,所以下面采用配置文件的方式再来一次:

根目录创建 webpack.config.js 文件,并配置下打包入口和出口:

// webpack-test/webpack.config.js
module.exports = {
  mode: "development",//webpack.0之后需要声明环境
  entry: __dirname + "/app/main.js",//唯一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放目录
    filename: "bundle.js"//打包后输出文件名
  }
}

再次打包的时候,只需要使用命令 webpack 就可以了,webpack 默认读取当前路径下的 webpack.config.js 文件。

最终打包好的 bundle.js 文件,去除了多余注释,调整了代码格式,内容如下:

// 自执行函数,参数为所有模块组成的,形势为key:value,key是模块名
(function(modules) { // webpackBootstrap
  // 已加载模块的缓存,记录模块的加载情况,也是为了避免重复打包,节省资源
  var installedModules = {};

  // webpack 使用 require 方式加载模块的方法(模拟ConmmonJS reqiure()),作用为根据传进来的模块id来处理对应的模块,加入已加载缓存,执行,标记,返回exports
  function __webpack_require__(moduleId) {
    // moduleId 为模块路径
    // 检测模块是否已加载,已加载的话直接返回该模块
    if(installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    // 当前模块未加载的话,新建,并存于缓存
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    };

    // 在当前模块的 exports 下,也就是模块的内部执行模块代码,突出作用域
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

    // 标记模块已经加载
    module.l = true;

    // 返回模块的导出内容
    return module.exports;
  }


  // 挂载属性,该模块 (__webpack_modules__)
  __webpack_require__.m = modules;

  // 挂载属性,模块加载缓存
  __webpack_require__.c = installedModules;

  // 本代码中未执行,暂时不分析
  // 在 exports 中定义 getter 方法
  __webpack_require__.d = function(exports, name, getter) {
    // 当 name 属性不是定义在对象本身,而是继承自原型链,则在在 exports 中定义 getter 方法
    if(!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, { enumerable: true, get: getter });
    }
  };

  // 本代码中未执行,暂时不分析
  // 在 exports 中定义 __esModule,定义key为Symbol的属性(在__webpack_require__.t中被调用)
  // define __esModule on exports
  __webpack_require__.r = function(exports) {
    if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    }
    Object.defineProperty(exports, '__esModule', { value: true });
  };

  // 本代码中未执行,暂时不分析
  // 创建一个伪命名空间的对象
  // create a fake namespace object
  // mode & 1: value is a module id, require it
  // mode & 2: merge all properties of value into the ns
  // mode & 4: return value when already ns object
  // mode & 8|1: behave like require
  __webpack_require__.t = function(value, mode) {
    if(mode & 1) value = __webpack_require__(value);
    if(mode & 8) return value;
    if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
    var ns = Object.create(null);
    __webpack_require__.r(ns);
    Object.defineProperty(ns, 'default', { enumerable: true, value: value });
    if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
    return ns;
  };

  // 本代码中未执行,暂时不分析
  // 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 = "";


  // 加载入口模块 main.js ,返回 exports,从而从入口文件开始执行,以递归的方式,将所有依赖执行并返回
  return __webpack_require__(__webpack_require__.s = "./app/main.js");
})({

  "./app/bye.js": (function(module, exports, __webpack_require__) {

    eval("const to = __webpack_require__( \"./app/to.js\");\nmodule.exports = function() {\n var bye = document.createElement('div');\n bye.textContent = \"Say Bye to \" + to.name;\n return bye;\n};\n\n//# sourceURL=webpack:///./app/bye.js?");

  }),

  "./app/hello.js": (function(module, exports) {

    eval("module.exports = function() {\n var hello = document.createElement('div');\n hello.textContent = \"Say Hello!\";\n return hello;\n};\n\n//# sourceURL=webpack:///./app/hello.js?");

  }),

  "./app/main.js": (function(module, exports, __webpack_require__) {

    eval("const hello = __webpack_require__( \"./app/hello.js\");\nconst bye = __webpack_require__( \"./app/bye.js\");\n\ndocument.querySelector(\"#root\").appendChild(hello()).appendChild(bye());;\n\n//# sourceURL=webpack:///./app/main.js?");

  }),

  "./app/to.js": (function(module, exports) {

    eval("module.exports = {name: \"小明\"};\n\n//# sourceURL=webpack:///./app/to.js?");

  })

});

分析

webpack 的运行过程可分为:读取配置参数,实例化插件,模块解析处理(loader),输出打包文件;在上面例子中,仅为 JavaScript 的引用,没有使用插件和像CSS、less、图片之类需要loader处理的模块,所以上面的例子,过程只有读取配置,识别入口及其引用模块,打包几步,生成最终的 bundle.js 文件。

简单描述下 webpack 在这个过程中的执行流程:在配置文件中读取入口,如果有配置 plugins 参数,那么也是在此时进行插件的实例化和钩子函数的绑定;模块解析,也就是loader加入的时刻,从入口文件开始,根据入口文件对其他模块的依赖,结合配置文件中对不同种类型文件所使用的 loader(加载器) 说明,一个一个逐级对这些模块进行解析处理,或压缩,或转义,生成浏览器可以直接识别的内容;最后将所有模块进行打包,输出打包后的文件。在上面的代码中,已经对 bundle.js 内容进行了内容注释,下面我们来分析下 bundle.js 的执行过程:

1、自执行函数

最后的输出的文件 bundle.js 是一个 JavaScript 文件,其本身其实是一个自执行函数

(function(参数){})(参数)。

2、参数

自执行方法的参数为所有模块组成的对象,key 为各个模块的路径,值为各模块内部的执行代码,观察参数内部的代码,对比打包前的源码,可以发现凡是 require 都变成了__webpack_require__这个webpack自定义的模块调用方法,而且源码中的相对路径也变成了最终执行位置的文件的相对路径。

{

  "./app/bye.js": (function(module, exports, __webpack_require__) {

    eval("const to = __webpack_require__( \"./app/to.js\");\nmodule.exports = function() {\n var bye = document.createElement('div');\n bye.textContent = \"Say Bye to \" + to.name;\n return bye;\n};\n\n//# sourceURL=webpack:///./app/bye.js?");

  }),

  "./app/hello.js": (function(module, exports) {

    eval("module.exports = function() {\n var hello = document.createElement('div');\n hello.textContent = \"Say Hello!\";\n return hello;\n};\n\n//# sourceURL=webpack:///./app/hello.js?");

  }),

  "./app/main.js": (function(module, exports, __webpack_require__) {

    eval("const hello = __webpack_require__( \"./app/hello.js\");\nconst bye = __webpack_require__( \"./app/bye.js\");\n\ndocument.querySelector(\"#root\").appendChild(hello()).appendChild(bye());;\n\n//# sourceURL=webpack:///./app/main.js?");

  }),

  "./app/to.js": (function(module, exports) {

    eval("module.exports = {name: \"小明\"};\n\n//# sourceURL=webpack:///./app/to.js?");

  })

}

3、执行

(1)自执行文件开始执行后,到自执行函数最底部,首先从入口文件开始加载

return __webpack_require__(__webpack_require__.s = "./app/main.js");

(2)__webpack_require__函数被调用,传入参数 ./app/main.js,

function __webpack_require__(moduleId) {
    // moduleId 为 ./app/main.js
    // 首次进来,未加载,模块还没有缓存
    if(installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    // 新建 ./app/main.js 模块,并存于缓存
    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;
  }

此时方法中执行 modules[moduleId].call(module.exports, module, module.exports,__webpack_require__); 相当于在名为 ./app/main.js 的模块中执行如下代码:

(function(module, exports, __webpack_require__) {

    eval("const hello = __webpack_require__( \"./app/hello.js\");\nconst bye = __webpack_require__( \"./app/bye.js\");\n\ndocument.querySelector(\"#root\").appendChild(hello()).appendChild(bye());;\n\n//# sourceURL=webpack:///./app/main.js?");

})()

由于引用关系,接下来会再次执行两次__webpack_require__方法,分别传参模块路径 ./app/hello.js 和 ./app/bye.js;

(3)执行第一个__webpack_require__过程,除了传参不同、执行的模块不同,与第二步基本一致,再次找到了依赖模块 to.js,再次调用__webpack_require__。

"./app/hello.js": (function(module, exports, __webpack_require__) {

  eval("const to = __webpack_require__( \"./app/to.js\");\nmodule.exports = function() {\n var hello = document.createElement('div');\n hello.textContent = \"Say Hello to \" + to.name;\n return hello;\n};\n\n//# sourceURL=webpack:///./app/hello.js?");

}),

(4)执行第二个__webpack_require__时,在 bye.js 中找到了对于 to.js 的依赖,所以将继续调用__webpack_require__方法,只是传参变成了./app/to.js,达到终点。

"./app/bye.js": (function(module, exports, __webpack_require__) {

    eval("const to = __webpack_require__( \"./app/to.js\");\nmodule.exports = function() {\n var bye = document.createElement('div');\n bye.textContent = \"Say Bye to \" + to.name;\n return bye;\n};\n\n//# sourceURL=webpack:///./app/bye.js?");

})

(5)到此时,整个从入口文件的开始的针对所依赖模块的解析已经完成,所有的 js 代码也已经引用完毕且放到了 bundle.js 中。

以上是“webpack4.x CommonJS模块化的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网node.js频道!

--结束END--

本文标题: webpack4.x CommonJS模块化的示例分析

本文链接: https://www.lsjlt.com/news/80949.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • webpack4.x CommonJS模块化的示例分析
    这篇文章主要为大家展示了“webpack4.x CommonJS模块化的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“webpack4.x Common...
    99+
    2022-10-19
  • CommonJS中模块加载的示例分析
    这篇文章主要介绍CommonJS中模块加载的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!叨叨一会CommonJSCommon这个英文单词的意思,相信大家都认识,我记得有一个...
    99+
    2022-10-19
  • webpack4.x开发环境配置的示例分析
    这篇文章将为大家详细讲解有关webpack4.x开发环境配置的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。具体如下:一、全局安装webpack如果我们按照旧版本...
    99+
    2022-10-19
  • webpack4的示例分析
    小编给大家分享一下webpack4的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!新建一个demo文件夹,然后再将命令行...
    99+
    2022-10-19
  • Webpack4 Tree Shaking优化的示例分析
    这篇文章将为大家详细讲解有关Webpack4 Tree Shaking优化的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。先说好处在讨论技术细节之前,让我先总结一...
    99+
    2022-10-19
  • JavaScript模块化的示例分析
    小编给大家分享一下JavaScript模块化的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1. 浏览器支持使用JavaScript 模块依赖于 impo...
    99+
    2023-06-15
  • vuex与模块化的示例分析
    这篇文章将为大家详细讲解有关vuex与模块化的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。示例教程例子是在vue-cli基础上构建的,以下是src文件下的内容目...
    99+
    2022-10-19
  • js模块化规范的示例分析
    这篇文章主要介绍js模块化规范的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1. CommonJS用于服务端模块化编程,比如nodejs就采用此规范;一个文件就是一个模块,...
    99+
    2022-10-19
  • webpack4图片处理的示例分析
    这篇文章给大家分享的是有关webpack4图片处理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。代码目录(如下图所示):讲述 webpack4 中的图片常用的基础操作:...
    99+
    2022-10-19
  • webpack3升级到webpack4的示例分析
    这篇文章主要介绍了webpack3升级到webpack4的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1 webpack升级到4....
    99+
    2022-10-19
  • 多页应用Webpack4配置优化的示例分析
    这篇文章将为大家详细讲解有关多页应用Webpack4配置优化的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前言webpack4 相比之前的 2 与 3,改变很大...
    99+
    2022-10-19
  • Vuex下Store模块化拆分的示例分析
    这篇文章给大家分享的是有关Vuex下Store模块化拆分的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。模块化拆分vue.js的项目文件结构在这里就不说了,大家可以通过v...
    99+
    2022-10-19
  • python模块的示例分析
    小编给大家分享一下python模块的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Python的优点有哪些1、简单易用,与C/C++、Java、C# 等传...
    99+
    2023-06-14
  • Vuex中模块化组织的示例分析
    这篇文章主要为大家展示了“Vuex中模块化组织的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Vuex中模块化组织的示例分析”这篇文章吧。在实际项目中如...
    99+
    2022-10-19
  • JS模块化知识点的示例分析
    小编给大家分享一下JS模块化知识点的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!模块化是一个语言膨胀的必经之路,它能够...
    99+
    2022-10-19
  • JavaScript模块模式的示例分析
    这篇文章主要介绍了JavaScript模块模式的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。具体如下:在JS中没有Class的概念...
    99+
    2022-10-19
  • Webpack4+Babel7+ES6兼容IE8的示例分析
    这篇文章主要介绍Webpack4+Babel7+ES6兼容IE8的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!ES6兼容IE8需要解决四个问题语法支持IE浏览器不支持ES6...
    99+
    2022-10-19
  • Python模块cachetools的示例分析
    Python模块cachetools的示例分析,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。前言cachetools 是一个 Python 模块,提供各种记忆集合和修饰符,包括...
    99+
    2023-06-02
  • JS前端模块化规范的示例分析
    这篇文章将为大家详细讲解有关JS前端模块化规范的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Script 标签其实最原始的 JavaScript 文件加载方式,...
    99+
    2022-10-19
  • webpack4之splitchunksPlugin代码包分拆的示例分析
    小编给大家分享一下webpack4之splitchunksPlugin代码包分拆的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作