Webpack入门,模块打包之CommonJS简介

CommonJS

说到前端的模块,不得不讲一下CommonJS。CommonJS是由JavaScript社区于2009年提出的包含模块、文件、IO、控制台在内的一系列标准。Node.js的实现中采用了CommonJS标准的一部分,并在其基础上进行了一些调整。真正的CommonJS模块和Node.js中实现并不完全一样,通常情况下,谈到的CommonJS其实是Node.js中的版本,而非它的原始定义。

CommonJS最初只为服务端而设计,直到有了Browserify——一个运行在Node.js环境下的模块打包工具,它可以将CommonJS模块打包为浏览器可以运行的单个文件。这意味着客户端的代码也可以遵循CommonJS标准来编写了。

何为模块

模块之于程序,就如同细胞之于生物体,是具有特定功能的组成单元。不同的模块负责不同的工作,它们以某种方式联系在一起,共同保证程序的正常运转。

CommonJS中规定每个文件是一个模块。将一个JavaScript文件直接通过script标签插入页面中于封装成CommonJS模块最大的区别在于,前者的顶层作用域是全局作用域,而进行变量及函数声明时会污染全局变量;而后者会形成一个属于模块自身的作用域,所有的变量及函数只有自己能访问,对外是不可见的。

举例为证

在工程中新建calculator.js文件与index.js文件

// calculator.js
var name = "calculator.js";
// index.js
var name = "index.js";
require("./calculator.js");
console.log(name);

这里有两个文件,在index.js中通过CommonJS的require函数加载calculator.js。运行之后控制台输出结果是“index.js”,说明calculator.js中的变量声明并不会影响index.js,可见每个模块是拥有各自的作用域的。

导出

导出是一个模块向外暴露自身的唯一方式。在CommonJS中,通过module.exports可以导出模块中的内容。

// calculator.js
module.exports = {
  name: "calculator",
  add: function(a,b) {
    return a + b;
  }
}

CommonJS模块内部会用一个module对象存放当前模块的信息,可以理解成在每个模块的最开始定义了以下对象:var module = {...}; module.exports = {...}

module.exports用来指定该模块要对外暴露的哪些内容,在上面的额代码中导出了一个对象,包含name和add两个属性。为了书写方便,CommonJS也支持另外一种简化的导出方式——直接使用exports

exports.name = "calculator.js";
exports.add = function(a,b) {
  return a + b;
}

在实际效果上,这段代码和上面的module.exports没有任何不同。其内在机制是将exports指向module.exports,而module.exports在初始化时是一个空对象。可以简单的理解为,CommonJS在每个模块的首部默认添加了以下代码

var module = {
  exports: {}
};
var exports = module.exports;

因此,为exports.add赋值相当于在module.exports对象上添加一个属性。

注意事项

在使用exports时,不要直接给exports赋值,否则会导致其失效。
不要混合使用module.exports与exports

导入

在commonJS中使用require语法进行模块导入。

// calculator.js
module.exports = {
  add: function(a,b) {
    return a + b;
  }
}
// index.js
const calculator = require("./calculator.js");
const sum = calculator.add(5,7);
console.log(sum);

在index.js中导入了calculator模块,并调用了它的add函数。

使用require导入一个模块时会有两种情况:

  • 该模块未曾被加载过。这时会首先执行该模块,然后获取到该模块最终导出的内容。
  • 该模块已经被加载过。这时该模块的代码不会再次执行,而是直接获取该模块上一次导出的内容。

有时在加载一个模块时,不需要获取其导出的内容,只是想要通过执行它而产生某种作用,比如把它的接口挂在全局对象上,此时直接使用require即可。

require函数可以接收表达式,借助这个特性我们可以动态地指定模块加载路径。

const moduleNames = ["foo.js","bar.js"];
moduleNames.forEach(name => {
  require('./' + name);
})

示例代码仓库

https://gitee.com/zero_79152105/webpack-vblog

原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/webpack-module-commonjs.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
ZEROZERO
上一篇 2022年11月10日
下一篇 2022年11月11日

相关推荐

  • Spring Boot的常用注解

    未来的框架趋势是“约定大于配置”,代码的封装会更加严密。开发人员会将更多的精力放在代码的整体优化和业务逻辑上,所以注解式编程会被更加广泛地使用。那么什么是注解?Spring Boo…

    2024年8月29日
    616
  • STS插件mybatis-generator安装及使用

    断断续续学习Java也有好长时间了,没有师傅带,没有项目练手,学习超级慢,也很烦。视频、书籍翻看了一大推,还是没有目标。 相信滴水成海,外加条条大路通罗马,只要坚持,自己终能达成目…

    2019年12月27日
    2.5K
  • CSS布局之圣杯与双飞翼布局

    所谓圣杯布局和双飞翼布局其实解决的问题是相同的,都是解决左右两栏固定宽度,中间部分自适应,其中某部分内容比其他内容高的时候,保证三者元素等高。他俩的区别就是:圣杯用padding。…

    2019年6月18日
    2.0K
  • 深入理解JS内存机制

    JS的内存机制在很多前端开发者看来并不是那么重要,但是如果你想深入学习JS,并将它利用好,打造高质量高性能的前端应用,就必须要了解JS的内存机制。对于内存机制理解了以后,一些基本的…

    2019年7月14日
    1.9K
  • Java自学之数组

    在Java中数组是一组相关变量的集合,属于引用数据类型。 定义 数组引用传递分析 数组属于引用数据类型,在数组使用时需要通过关键字new开辟堆内存空间,一块堆内存空间也可以同时被多…

    2020年11月26日
    1.5K
  • Java自学之类结构扩展

    面向对象中的核心组成是类与接口,在项目中会利用【包】进行一组相关类的管理,这样适合于程序代码的部分更新,也更加符合面向对象封装性的概念,同时合理地使用封装也可以方便地实现实例化对象…

    2020年12月10日
    1.5K
  • 如何搭建MyBatis开发环境

    进入一段时间的学习及温习,已经可以说是初步掌握了Javaweb入门开发,由于我的中心思想是抛弃JSP,做纯粹的前后端分离项目,所以接下来计划学习持久层开发,现在主流的持久层开发工具…

    2022年4月6日
    922
  • 如何封装VUE组件库?

    之前一直在使用Angular开发项目,也封装过Angular组件。由于种种原因,现需要转战VUE。好在本人有扎实的实战经验,结合各位网络大神整理的经验,现总结一篇关于封装VUE组件…

    2019年7月31日
    2.5K
  • spring4.x学习之创建工程

    断断续续学习Java有两三个月了,目前还是处于入门阶段。关于java及spring那些的设计理念方面的理论知识,不花费大量精力及时间是看不懂的(至少对于我这么一个前端转后端的初学者…

    2019年3月19日
    2.0K
  • Java自学之泛型

    在Java语言中,为了方便接收参数类型的统一,提供了核心类Object,利用此类对象可以接收所有类型的数据(包括基本数据类型和引用数据类型)。但是由于其所描述的数据范围过大,所以在…

    2020年12月8日
    1.5K