Javascript 模块化指北

前言

随着 Web 技术的蓬勃发展和依赖的基础设施日益完善,前端领域逐渐从浏览器扩展至服务端(Node.js),桌面端(PC、Android、iOS),乃至于物联网设备(IoT),其中 JavaScript 承载着这些应用程序的核心部分,随着其规模化和复杂度的成倍增长,其软件工程体系也随之建立起来(协同开发、单元测试、需求和缺陷管理等),模块化编程的需求日益迫切。JavaScript 对模块化编程的支持尚未形成规范,难以堪此重任;一时间,江湖侠士挺身而出,一路披荆斩棘,从刀耕火种过渡到面向未来的模块化方案;

概念

模块化编程就是通过组合一些__相对独立可复用的模块__来进行功能的实现,其最核心的两部分是__定义模块__和__引入模块__;

  • 定义模块时,每个模块内部的执行逻辑是不被外部感知的,只是导出(暴露)出部分方法和数据;
  • 引入模块时,同步 / 异步去加载待引入的代码,执行并获取到其暴露的方法和数据;

刀耕火种

尽管 JavaScript 语言层面并未提供模块化的解决方案,但利用其可__面向对象__的语言特性,外加__设计模式__加持,能够实现一些简单的模块化的架构;经典的一个案例是利用单例模式模式去实现模块化,可以对模块进行较好的封装,只暴露部分信息给需要使用模块的地方;

// Define a module
var moduleA = (function ($, doc) {var methodA = function() {};var dataA = {};return {methodA: methodA,dataA: dataA};
})(jQuery, document);// Use a module
var result = moduleA.mehodA();

直观来看,通过立即执行函数(IIFE)来声明依赖以及导出数据,这与当下的模块化方案并无巨大的差异,可本质上却有千差万别,无法满足的一些重要的特性;

  • 定义模块时,声明的依赖不是强制自动引入的,即在定义该模块之前,必须手动引入依赖的模块代码;
  • 定义模块时,其代码就已经完成执行过程,无法实现按需加载;
  • 跨文件使用模块时,需要将模块挂载到全局变量(window)上;

AMD & CMD 二分天下

题外话:由于年代久远,这两种模块化方案逐渐淡出历史舞台,具体特性不再细聊;

为了解决”刀耕火种”时代存留的需求,AMD 和 CMD 模块化规范问世,解决了在浏览器端的异步模块化编程的需求,__其最核心的原理是通过动态加载 script 和事件监听的方式来异步加载模块;__

AMD 和 CMD 最具代表的两个作品分别对应 require.js 和 sea.js;其主要区别在于依赖声明和依赖加载的时机,其中 require.js 默认在声明时执行, sea.js 推崇懒加载和按需使用;另外值得一提的是,CMD 规范的写法和 CommonJS 极为相近,只需稍作修改,就能在 CommonJS 中使用。参考下面的 Case 更有助于理解;

// AMD
define(['./a','./b'], function (moduleA, moduleB) {// 依赖前置moduleA.mehodA();console.log(moduleB.dataB);// 导出数据return {};
});// CMD
define(function (requie, exports, module) {// 依赖就近var moduleA = require('./a');moduleA.mehodA();     // 按需加载if (needModuleB) {var moduleB = requie('./b');moduleB.methodB();}// 导出数据exports = {};
});

CommonJS

2009 年 ry 发布 Node.js 的第一个版本,CommonJS 作为其中最核心的特性之一,适用于服务端下的场景;历年来的考察和时间的洗礼,以及前端工程化对其的充分支持,CommonJS 被广泛运用于 Node.js 和浏览器;

// Core Module
const cp = require('child_process');
// Npm Module
const axios = require('axios');
// Custom Module
const foo = require('./foo');module.exports = { axios };
exports.foo = foo;

规范

  • module (Object): 模块本身
  • exports (*): 模块的导出部分,即暴露出来的内容
  • require (Function): 加载模块的函数,获得目标模块的导出值(基础类型为复制,引用类型为浅拷贝),可以加载内置模块、npm 模块和自定义模块

实现

1、模块定义

默认任意 .node .js .json 文件都是符合规范的模块;

2、引入模块

首先从缓存(require.cache)优先读取模块,如果未命中缓存,则进行路径分析,然后按照不同类型的模块处理:

  • 内置模块,直接从内存加载;
  • 外部模块,首先进行文件寻址定位,然后进行编译和执行,最终得到对应的导出值;

其中在编译的过程中,Node对获取的JavaScript文件内容进行了头尾包装,结果如下:

(function (exports, require, module, __filename, __dirname) {var circle = require('./circle.js');console.log('The area of a circle of radius 4 is ' + circle.area(4));
});

特性总结

  • 同步执行模块声明和引入逻辑,分析一些复杂的依赖引用(如循环依赖)时需注意;
  • 缓存机制,性能更优,同时限制了内存占用;
  • Module 模块可供改造的灵活度高,可以实现一些定制需求(如热更新、任意文件类型模块支持);

ES Module(推荐使用)

ES Module 是语言层面的模块化方案,由 ES 2015 提出,其规范与 CommonJS 比之 ,导出的值都可以看成是一个具备多个属性或者方法的对象,可以实现互相兼容;但写法上 ES Module 更简洁,与 Python 接近;

import fs from 'fs';
import color from 'color';
import service, { getArticles } from '../service'; export default service;
export const getArticles = getArticles;

主要差异在于:

  • ES Module 会对静态代码分析,即在代码编译时进行模块的加载,在运行时之前就已经确定了依赖关系(可解决循环引用的问题);
  • ES Module 关键字:import export 以及独有的 default 关键字,确定默认的导出值;
  • ES Module 中导出的值是一个 只读的值的引用 ,无论基础类型和复杂类型,而在 CommonJS 中 require 的是值的拷贝,其中复杂类型是值的浅拷贝;
// a.js
export let a = 1;
export function caculate() {a++;
};// b.js
import { a, caculate } from 'a.js';console.log(a); // 1
caculate();
console.log(a); // 2a = 2; // Syntax Error: "a" is read-only

UMD

通过一层自执行函数来兼容各种模块化规范的写法,兼容 AMD / CMD / CommonJS 等模块化规范,贴上代码胜过千言万语,需要特别注意的是 ES Module 由于会对静态代码进行分析,故这种运行时的方案无法使用,此时通过 CommonJS 进行兼容;

(function (global, factory) {if (typeof exports === 'object') {   module.exports = factory();} else if (typeof define === 'function' && define.amd) {define(factory);} else {this.eventUtil = factory();}
})(this, function (exports) {​ // Define ModuleObject.defineProperty(exports, "__esModule", {value: true});exports.default = 42;
});

构建工具中的实现

为了在浏览器环境中运行模块化的代码,需要借助一些模块化打包的工具进行打包( 以 webpack 为例),定义了项目入口之后,会先快速地进行依赖的分析,然后将所有依赖的模块转换成浏览器兼容的对应模块化规范的实现;

模块化的基础

从上面的介绍中,我们已经对其规范和实现有了一定的了解;在浏览器中,要实现 CommonJS 规范,只需要实现 module / exports / require / global 这几个属性,由于浏览器中是无法访问文件系统的,因此 require 过程中的文件定位需要改造为加载对应的 JS 片段(webpack 采用的方式为通过函数传参实现依赖的引入)。

webpack 打包出来的代码快照如下,注意看注释中的时序;

(function (modules) {// The module cachevar installedModules = {};// The require functionfunction __webpack_require__(moduleId) {}return __webpack_require__(0); // ---> 0
})
({0: function (module, exports, __webpack_require__) {// Define module Avar moduleB = __webpack_require__(1); // ---> 1},1: function (module, exports, __webpack_require__) {// Define module Bexports = {}; // ---> 2}
});

实际上,ES Module 的处理同 CommonJS 相差无几,只是在定义模块和引入模块时会去处理 __esModule 标识,从而兼容其在语法上的差异。

异步和扩展

1、浏览器环境下,网络资源受到较大的限制,因此打包出来的文件如果体积巨大,对页面性能的损耗极大,因此需要对构建的目标文件进行拆分,同时模块也需要支持动态加载;

webpack 提供了两个方法 require.ensure() 和 import() (推荐使用)进行模块的动态加载,至于其中的原理,跟上面提及的 AMD & CMD 所见略同,import() 执行后返回一个 Promise 对象,其中所做的工作无非也是动态新增 script 标签,然后通过 onload / onerror 事件进一步处理。

2、由于 require 函数是完全自定义的,我们可以在模块化中实现更多的特性,比如通过修改 require.resolve 或 Module._extensions 扩展支持的文件类型,使得 css / .jsx / .vue / 图片等文件也能为模块化所使用;


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/519054.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

对话鲁直:蚂蚁金服中间件的开源头羊 | 穿山甲专访

谈话中,鲁直反问的“你为什么不开源?”这句话让我印象深刻。-- 老王 二月初春,在西子湖畔的细雨中,我拜访了蚂蚁金服中间件团队,和 SOFA 技术负责人鲁直做了一次深入交谈,更妙的是,鲁直也是负责…

java.net.NoRouteToHostException: Cannot assign requested address

启动脚本 jmeter.sh -n -t test.jmx -l result.jtl参数说明 - h 帮助 -> 打印出有用的信息并退出 - n 非GUI模式 -> 在GUI模式下进行JMeter测试 - t 测试文件 要运行的JMeter测试脚本文件 - l 日志文件 记录结果的文件 - r 远程执行 启动远程服务 - H 代理主机 设置JMete…

与时间赛跑:微盟的数据恢复为什么需要这么长时间

作者| 茹炳晟责编 | Carol出品| CSDN云计算(ID:CSDNcloud)微盟“删库跑路“事件已经过去好几天了,据悉,微盟的服务已经全部恢复,对于新用户,已经能够正常开始所有相关的业务活动了,但…

基于Dockerfile创建一个最简单的docker镜像

试着创建一个docker image, 不在乎实用性和功能,只是对docker 创建image 有个大概的了解,所以有此文章。 文章目录step1. 创建文件夹step2. 进入文件夹step3. 创建Dockerfilestep4. 构建镜像step1. 创建文件夹 mkdir busyboxstep2. 进入文件夹 cd busyb…

阿里开发者招聘节 | 面试题08:NAS(Network Attached Storage)协议NFS和SMB相关问题

为帮助开发者们提升面试技能、有机会入职阿里,云栖社区特别制作了这个专辑——阿里巴巴资深技术专家们结合多年的工作、面试经验总结提炼而成的面试真题这一次将陆续放出(面试题官方参考答案将在专辑结束后统一汇总分享,点此进入答题并围观他…

android studio n3710,英特尔奔腾N3710性能跑分和评测 | ZMMOO

英特尔奔腾N3710 是针对入门级笔记本电脑,它的频率为1.1- 2.56 GHz。由于采用三栅极晶体管的新型14纳米低功耗工艺(P1273),与其前代Bay Trail(例如奔腾 N3520)相比,能效得到了显着提升 。除了CPU内核之外,SoC还提供支持DirectX 11…

淘宝千万级并发架构的十四次演进

责编 | Carol来源 | 架构师技术联盟概述本文以淘宝作为例子,介绍从一百个到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原…

linux 15秒 搭建VSFTPD文件服务器

文章目录1. 在线安装2. 创建用户3. 限制用户活动范围4. 添加用户管制5. 关闭匿名访问6. 编辑一个安全文件7. 重新启动vsftpd8. 客户端测试9. 本机测试10. 再次本机测试1. 在线安装 yum install vsftpd2. 创建用户 useradd ftpuser passwd ftpuser3. 限制用户活动范围 vim vs…

阿里开源!轻量级深度学习端侧推理引擎 MNN

AI科学家贾扬清如此评价道:“与 Tensorflow、Caffe2 等同时覆盖训练和推理的通用框架相比,MNN 更注重在推理时的加速和优化,解决在模型部署的阶段的效率问题,从而在移动端更高效地实现模型背后的业务。这和服务器端 TensorRT 等推…

冰雪复古鸿蒙碎片哪里出,冰雪复古:玩法攻略,如何高爆率搞回收!

原标题:冰雪复古:玩法攻略,如何高爆率搞回收!冰冻传说这个手游游戏刚内部测试的时候我就开始玩这个游戏了,第一方面这个游戏全部的装备都是靠打野怪获取的,这点很符合我的喜好,现在分享一些在个…

与时间赛跑:微盟的数据恢复为什么需要这么长时间?

作者| 茹炳晟 责编 | Carol 出品| CSDN云计算(ID:CSDNcloud) 微盟“删库跑路“事件已经过去好几天了,据悉,微盟的服务已经全部恢复,对于新用户,已经能够正常开始所有相关的业务活动了&#x…

linux shell读取配置文件中的内容

文章目录1. 创建conf文件夹2. 创建sftp_conn.conf文件3. 编写读取文件的脚本4. 赋予脚本可执行权限5. 执行脚本6. 获取的信息7. 利用这种方式 举一反三读取sftp服务器配置信息 1. 创建conf文件夹 mkdir conf2. 创建sftp_conn.conf文件 cd conf vim sftp_conn.conf 写入内容 格…

X-Pack Spark归档POLARDB数据做分析

简介 POLARDB数据库是阿里云自研的下一代关系型云数据库,100%兼容MySQL,性能最高是MySQL的6倍,但是随着数据量不断增大,面临着单条SQL无法分析出结果的现状。X-Pack Spark为数据库提供分析引擎,旨在打造数据库闭环&am…

阿里云Kubernetes服务上使用Tekton完成应用发布初体验

Tekton 是一个功能强大且灵活的 Kubernetes 原生开源框架,用于创建持续集成和交付(CI/CD)系统。通过抽象底层实现细节,用户可以跨多云平台和本地系统进行构建、测试和部署。 本文是基于阿里云Kubernetes服务部署Tekton Pipeline&…

在线教育如何应对流量洪峰?阿里云专家:上云+云数据库是最佳路径

2月中下旬原本是全国各地春季学期开学的日子,但这场突如其来的疫情使得1.8亿中小学生只能纷纷在家开启“停课不停学”的学习生活,而线上教育也顺势成为了这一特殊时期首选的学习方式。 但突如其来的流量洪峰,却让许多线上教育平台力不从心。…

适用于ios和android,适用于iOS和Android的OpenGL ES差异

我有一个适用于iOS和Android的OpenGL ES应用程序.大多数代码是很久以前由另一个人编写的,现在我必须维护它. OpenGL的使用看起来相当简单(游戏是2D,只使用纹理精灵以简单的方式).但我发现iOS和Android的图形代码实现存在两个主要差异:1)iOS代码包含以下代码&#xf…

linux 统计当前目录下文件或者文件夹的数量

文章目录一、文件1. 统计当前目录下文件的数量(不包括子文件夹中的文件)2. 统计当前目录下文件的数量(包括子文件夹中的文件)3. 查看特定目录内的文件数量二、文件夹2.1. 查看当前目录内的目录数量(不包含子目录中的目录)2.2. 查看当前目录内的目录数量(包含子目录中的目录)ls …

会 SQL 就能搞定 AI!蚂蚁金服重磅开源机器学习工具 SQLFlow

5 月 6 日,在QCon 全球软件开发大会(北京站)2019上,蚂蚁金服副 CTO 胡喜正式宣布开源机器学习工具 SQLFlow,他在演讲中表示:“未来三年,AI 能力会成为每一位技术人员的基本能力。我们希望通过开…

什么是 CD 管道?一文告诉你如何借助Kubernetes、Ansible和Jenkins创建CD管道!

作者 | Magalix翻译 | 火火酱,责编 | Carol来源 | 架构师技术联盟封图 | CSDN付费下载于IC photoCI/CD要解决的是什么问题?CI/CD(CI全名Continuous Integration,持续集成;CD全名Continuous Deployment,持续…

edge中以右键“打印”的方式“保存”当前页面的pdf形式,下载过程中卡进度的问题

目录 问题描述: 可能的问题: 解决: 问题描述: 特殊情况下需要保存网页的pdf形式,但页面没有类似“导出pdf”的功能按钮,可以通过页面右键“打印”的方式“保存”当前页面的pdf形式。在pdf文件下载过程中出…