摘要:在日常进行JS/TS项目开发的时候,经常会遇到require某个依赖和module.exports来定义某个函数的情况。就很好奇Modules都代表什么和有什么作用呢。

本文分享自华为云社区《JS/TS项目里的Module都是什么?都有几种形式?loaders和bundlers的区别是什么?》,作者: gentle_zhou 。

在日常进行JS/TS项目开发的时候,经常会遇到require某个依赖和module.exports来定义某个函数的情况。再加上在日常审视代码的时候,发现tsconfig.json文件里有一个"compilerOptions",里面关于module引入的是"commonjs",就很好奇Modules都代表什么和有什么作用呢。

什么是Module?

一个Module(模块)顾名思义就是一段可以重复利用的代码(通常是一个特性,或则一些特性的集合;可以是一个文件或则多个文件/文件夹的集合),它封装了内部代码实现的细节并曝露一个公开的API,让其他代码可以轻易地加载和使用。

为什么我们需要Modules?

技术上来说,其实完成一个JS/TS项目,我们并不需要模块,直接上手写代码也是可以的。但就像在JAVA、Python软件项目里,不引入依赖一样,会导致程序员们重复写很多相同的代码。

引入Module,为的就是可以应对JS/TS项目的代码越来越庞大,越来越复杂的情形。我们需要使用软件工程的方法,来管理JS/TS项目的业务逻辑。

在JS/TS项目中,模块应该允许我们实现以下功能:

  • 抽象代码:将功能委托给专门的库,这样我们就不必了解它们内部实际如何实现的(无论多复杂)
  • 封装代码:如果我们不想再更改代码了,可以将代码隐藏在模块中
  • 重用代码:避免反复编写相同的代码
  • 管理依赖:在不重写代码的情况下,轻松改变依赖关系

几种常见的Module形式

在 ES6 Module 出现之前,在ES5时期,JS并没有提供一个官方的定义模块的规则;因此JavaScript 社区里的天才程序员们尝试了各种形式来定义模块,以达到“在现有的运行环境下,可以实现模块效果”的目的。

一些非常有名的模块形式:

  • CommonJS
    CommonJS形式是用在Node.js环境里的,我在文章开头提到的require和module.exports就是CommonJS里用来定义依赖和模块的:
  var dep1 = require('./dep1');
module.exports = function(){ // ...}
  • Asynchronous Module Definition (AMD)
    AMD(官方github链接)则是用在浏览器中的,顾名思义这个形式是异步的,其中用define函数来定义模块:
  // 一个依赖数组&一个工厂函数以参数的形式调用define函数
define(['dep1', 'dep2'], function (dep1, dep2) {
//通过返回一个值来定义模块值
return function () {};
});
  • Universal Module Definition (UMD)
    UMD则是可以用在浏览器和Node.js中,是通用的:
 (function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. 以同步模块的方式注册.
define(['b'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node节点. 不能和严格意义上的CommonJS一起使用,但是类似CommonJS的环境里是支持使用module.expoerts的,就像node.
module.exports = factory(require('b'));
} else {
// 浏览器 globals (根节点是window)
root.returnExports = factory(root.b);
}
}(this, function (b) {
// 返回一个值来定义module export;这里返回的是一个对象,但是模块其实可以返回一个函数作为exported value.
return {};
}));

以及现在出现的官方ES6 模块形式,一种原生的模块形式。它用export来输出模块的公开API:

// 输出函数
export function sayHello(){
console.log('Hello');
}

我们可以使用import和as来引入部分代码到模块里:

import { sayHello as say } from './lib';

say(); // 输出Hello

或则直接在一开始引入整个模块:

import * as lib from './lib';

lib.sayHello();  // 输出 Hello

Module loaders和Module bundlers的区别

两者都是为了让我们编写模块化JS/TS应用的时候更方便快捷。

Module loaders

模块加载器用来解析并加载以特定模块格式编写的模块,通常是一些库;可以加载、解释和执行使用特定模块格式/语法定义的JavaScript模块,比如AMD或Common JS。

在编写模块化JS/TS应用程序时,通常每个模块都有一个文件。因此,当编写由数百个模块组成的应用程序时,要确保所有文件都以正确的顺序包含进去可能会非常痛苦。所以,如果有加载器会为你负责依赖管理,确保所有模块在应用程序执行时被加载,那会轻松容易很多。

模块加载器是在运行时(runtime)运行的:

  • 在浏览器中加载模块加载器
  • 告诉模块加载器加载哪个主应用文件
  • 模块加载器下载并解析主应用文件
  • 模块加载器根据需要去下载文件

如果你试着在浏览器的开发人员控制台中打开network选项卡,将看到许多文件是按需由模块加载器加载的:

一些流行的模块加载器的例子如下:

  • Require JS: AMD格式的模块加载器
  • System JS: AMD, Common JS, UMD或System.register格式的模块加载器

Module bundlers

模块绑定器相当于是模块加载器的替代品;基本上,它们做的事情是一样的(管理和加载相互依赖的模块)。

但模块绑定器和加载器不同的地方是,它并非是在运行时运行的,而是作为应用程序构建的一部分运行(在build的时候运行);而且它是在浏览器中加载的。因此,绑定器在执行代码之前会将所有模块合并到一个文件/bundle中(比如叫bundle.js),而不是在代码运行时再去加载出现的依赖项。比如现在流行的两个bundlers:Webpack(AMD,Common JS, es6模块的bundler)和Browserify(Common JS模块的bundler)。

什么时候更适合用哪个呢?

这个问题的答案取决于JS/TS应用程序的结构与大小。

使用bundler的主要优点是,它让浏览器需要下载的文件变少了很多,这可以给我们的应用程序带来性能上的优势(因为减少了加载所需的时间);但是取决于应用程序的模块数量,并不是说用bundler就一定是最好的。对于那种大型应用(有很多模块),模块加载器可以提供更好的性能,因为bundler在一开始加载一个巨大的单文件会阻碍应用的启动。

如何选取,其实只需要我们进行测试比较一下即可~

参考链接

  1. https://v8.dev/features/modules
  2. https://www.geeksforgeeks.org/node-js-modules/
  3. https://www.jvandemo.com/a-10-minute-primer-to-javascript-modules-module-formats-module-loaders-and-module-bundlers/
  4. https://stackoverflow.com/questions/38864933/what-is-difference-between-module-loader-and-module-bundler-in-javascript

点击关注,第一时间了解华为云新鲜技术~

JS/TS项目里的Module都是什么?的更多相关文章

  1. 初始化构建React+Ts项目时出现:Module build failed (from ./node_modules/css-loader/dist/cjs.js): CssSyntaxError

    具体错误 ERROR in ./src/index.tsx Module build failed (from ./node_modules/css-loader/dist/cjs.js): CssS ...

  2. 如何在TypeScript/JavaScript项目里引入MD5校验和

    摘要:MD5校验和则是其中一种数学算法,通常是使用工具对文件计算得出的一组32 个字符的十六进制字母和数字. 本文分享自华为云社区<TypeScript/JavaScript项目里如何做MD5校 ...

  3. 今天出现了一个问题,Tomcat 进入localhost:8080正常,进入项目内别的页面都是空白页

    经仔细检查发现代码没有任何的问题,经仔细检查找到了原因. 问题原因:拦截器(过滤器)把我的访问请求全都拦下了,我在拦截器里把//chain.doFilter(request, response);这行 ...

  4. Angular项目里Js代码里如何获取Ts文件中的属性数据

    基于之前实现的Angular+ngx-ueditor富文本编辑器做一个简单补充记录,我们在使用Angular开发过程中,难免会使用到调用外部插件Js的应用,但是有的时候又需要在Js文件中调用Ts文件里 ...

  5. 关于common.js里面的module.exports与es6的export default的思考总结

    背景 公司项目需要裁切功能,基于第三方图片裁切组件vue-cropper(0.4.0版本),封装了图片裁切组件(picture-cut)(放在公司内部组件库,仅限于公司内部使用) 在vue-cropp ...

  6. Android studio 添加引用Module项目 与 设置Module项目的Libs的Jar在主项目里使用

    前言 添加引用Module项目 设置Module项目的Libs的Jar在主项目里使用 1.在项目里添加libs包,并且加入jar 2.设置这个module项目的build.gradle depende ...

  7. 项目里的jquery.min.js错误

    项目里的jquery.min.js报一系列 - Missing semicolon - Missing semicolon - Missing semicolon - Missing semicolo ...

  8. 教你如何解决JS/TS里特定String进行拆分然后遍历各个元素

    摘要:我们需要先判断特定String里是否包含我们需要的元素,针对这个元素对这个字符串进行拆分,遍历各个元素. 本文分享自华为云社区<JavaScript/TypeScript项目里如何对特定S ...

  9. 前端 JS/TS 调用 ASP.NET Core gRPC-Web

    前言 在上两篇文章中,介绍了ASP.NET Core 中的 gRPC-Web 实现 和 在 Blazor WebAssembly 中使用 gRPC-Web,实现了 Blazor WebAssembly ...

随机推荐

  1. 内置方法 __new__ __del__

    1.__new__ 构造方法 实例化对象是先执行__new__方法,但是类中没有__new__方法,所以先到父类object类中的new方法,开辟一个属于对象的空间,然后再执行init方法 设计模式: ...

  2. fastcgi未授权访问及任意命令执行

    1. 漏洞原理 服务端使用fastcgi协议并对外网开放9000端口,攻击者可以构造fastcgi协议包内容,实现未授权访问服务端.php文件以及执行任意命令. 2. 漏洞利用 第一步 搭建vulhu ...

  3. sql高级手工注入

    非常重要:首先在网站找到管理入口,否则,呵呵就算有用户名和密码,找不到入口,也是白玩.. 注入时,注意通过改变大小写.编码.转换等方式躲过系统检查,顺利执行语句!!! (一)数字型注入 正常步骤: 1 ...

  4. java反射笔记(学习尚硅谷java基础教程)

    反射一.概述:Reflection Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性 ...

  5. overflow原理?

    overflow: hidden能清除块内子元素的浮动影响. 因为该属性进行超出隐藏时需要计算盒子内所有元素的高度, 所以会隐式清除浮动 创建BFC条件(满足一个): float的值不为none: o ...

  6. 讲讲 kafka 维护消费状态跟踪的方法?

    大部分消息系统在 broker 端的维护消息被消费的记录:一个消息被分发到 consumer 后 broker 就马上进行标记或者等待 customer 的通知后进行标记.这 样也可以在消息在消费后立 ...

  7. redis 持久化有几种方式?

    面试题 redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的? 面试官心理分析 redis 如果仅仅只是将数据缓存在内存里面,如果 redis 宕机了再重启 ...

  8. memcached 与 redis 的区别?

    1.Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储.而 memcache 只支持简单数据类型,需要客户端自己处理复 杂对象 2.R ...

  9. 什么是 Spring 的 MVC 框架?

    Spring 配备构建 Web 应用的全功能 MVC 框架.Spring 可以很便捷地和其他 MVC 框架集成,如 Struts,Spring 的 MVC 框架用控制反转把业务对象和控制逻 辑清晰地隔 ...

  10. ArrayList、LinkedList、Vector、Array

    ArrayList 本质是一个数组. 优势:追加元素到数组末尾的时候速度快,同时检索元素的速度也快. 劣势:如果要插入一个元素到数组之间慢:如果要追加的元素数量多于数组的容量,则需要频繁扩容使用Arr ...