[前端]模块化开发

[前端]模块化开发

  • 为什么需要模块化开发
  • 模块化开发的目标
  • 模块化开发的演化
    • 文件划分方案
    • 对象封装方案
    • 立即执行函数
  • 模块化开发规范
    • CommonJS 规范
      • 特点
      • 定义模块
      • 全局对象 global
      • 导出模块(变量或方法)
      • 导入模块
    • AMD 规范
      • 特点
      • 定义模块
      • 导出模块(变量或方法)
        • 导出对象
        • 导出函数
        • 导出符合 CommonJs 规范的接口
      • 导入模块
        • require 导入方式
        • define 导入方式
    • CMD 规范
      • 定义模块
      • 导出模块(变量或方法)
      • 导入模块
    • UMD 规范
    • ES6 规范
      • 特点
      • 局限性
      • 导出与导入模块
  • 规范的实践案例
  • 规范的混合使用

为什么需要模块化开发

随着时代的发展,系统功能的不断增加,导致代码体量的不断增加,随着代码的增加,引发了一系列问题,例如:代码的耦合性,代码的加载顺序,代码的命名冲突问题等,这些问题导致了模块化的开发已经成为不可避免的趋势。

模块化开发的目标

模块化开发的目标,是为了解决代码开发中遇到的各种问题,但是前端开发的关注点不是完全基于文件类型分离的,前端工程化的最终目的都是为了能够更好地维护代码,因此这些目标必将成为模块化开发的优点,也是模块开发演化过程中不断追求的结果

  • 避免变量污染、命名冲突等
  • 提高代码复用率
  • 提高代码维护性
  • 方便依赖关系的管理

模块化开发的演化

主要是在模块化实践过程中,为实现模块化而出现的不同方案

文件划分方案

随着代码的不断增多,即使将每个功能和相关的一些状态数据单独存放到不同的文件当中,让每个文件像是是一个独立的模块,但是在使用的时候,通过 script 标签引入文件,导致所有模块都暴露在全局范围内工作,引起一系列问题:

  • 污染全局作用域
  • 命名冲突问题
  • 无法管理模块之间的依赖关系

对象封装方案

将变量或者函数等模块成员封装在对象中,当要使用的时候就调用这个对象的属性,但是仍旧存在问题:

  • 没有私有属性,模块成员可以在外部被访问或修改
  • 无法管理模块之间的依赖关系

立即执行函数

采用该方式为模块提供私有成员,因为在 javascript 中,只有函数内部的子函数才能读取局部变量,因此函数提供的私有作用域可确保函数中私有成员的安全,私有成员只能通过闭包的形式访问

在 javascript 中,只有函数内部的子函数才能读取局部变量

模块化开发规范

随着对模块化开发需求的增加,模块化开发形成了一些列的标准,并且这些标准也是在不断的发展的,目前的模块化开发标准包括:AMD 规范,CMD 规范,CommonJS 规范,ES6 规范。

模块化开发,肯定包括定义模块与使用模块;相同文件中可以直接使用定义的模块;在不同的文件中使用该模块是需要先导入该模块的,而被导入的模块必须是允许被导入的,也即该模块是被导出的。

所以下面将分别介绍不同规范是如何定义模块,导出与导入模块的,

CommonJS 规范

根据 CommonJS 规范实现的库或 API 是同步加载模块,CommonJS 是服务器端规范。

在服务器端,模块文件都存在本地磁盘,读取速度快,因此同步加载在服务器端不会有影响;

在浏览器端,由于网速等问题的影响,可能文件的加载需要较长时间,所以 AMD、CMD 异步加载方案更合理;

特点

  1. 所有的代码都在独立的模块作用域中,不会污染全局作用域
  2. 模块加载的顺序,按照其在代码中的引入顺序加载。
  3. 模块可以多次加载,但是只会在第一次加载时运行一次,运行结果被缓存。之后加载从缓存中直接读取,清空缓存重新运行。
  4. module.exports 属性输出是值拷贝。一旦操作完成,模块内发生的任何变化不会影响到已经输出的值。

定义模块

通过 module 对象来定义一个模块;

module 属性说明

module = {id: "", //模块的识别符,通常是带有绝对路径的模块文件名。filename: "", //模块的文件名,带有绝对路径。loaded: "", //返回一个布尔值,表示模块是否已经完成加载。parent: "", //返回一个对象,表示调用该模块的模块。children: "", //返回一个数组,表示该模块要用到的其他模块。exports: "" //表示模块对外输出的值。
};

全局对象 global

CommonJS 规范的全局对象是 global(类似于浏览器环境下的 window 对象),因此 global 对象上的变量或方法对其它文件是可获取的。

例如,

global.a = 1;

变量 a 被添加到了 global 上,在任意文件内都可以访问,该方式在模块化开发中是极不推荐的

导出模块(变量或方法)

模块导出的可以是变量或方法,CommonJS 通过 module.exports 暴露变量与方法;

let a = 1;function getA() {//moduleAreturn a++;
}module.exports.getA = getA;
//等价
exports.getA = getA;
//因为 exports = module.exports;

exports = ‘Hello World’||{}, exports 直接赋值,该操作切断了被赋值的 exports 与 module.exports 的联系,因此不可直接赋值!!! module.exports 可直接赋值

const a = 1;module.exports = function () {return a;
};

导入模块

通过 require 来引入其它模块暴露的值;

let a = require("./moduleA"); //2
let b = require("./moduleA"); //2 使用被缓存的结果

通过 require,同一个模块只有第一个加载时运行一次,然后运行结果被缓存,其它的加载都使用被缓存的结果

AMD 规范

AMD,Async Module Define 规范就是为了解决 CommonJS 实时加载,同步执行所带来的的弊端而出现的,它兼容 CommonJS 规范,因此符合 CommonJS 规范的模块可以被 AMD 规范正确导入。

至于什么是同步与异步,不了解的可以自行查询

根据 AMD 规范实现的库或 API 是异步加载模块,异步加载即非阻塞加载,更加适合浏览器端

特点

  1. 模块的加载是异步的

定义模块

通过 define 对象来定义一个模块;

define(id, [dependencies], factory);
  • id:可选,即模块名,该参数省略情况下自动使用文件名
  • dependencies:可选,依赖列表,数组每项是依赖的模块路径或模块名
  • factory:必需,工厂方法,初始化模块需要执行的函数或对象。如果为函数,它只被执行一次。如果是对象,此对象会作为模块的输出值

导出模块(变量或方法)

通过 define 来导出模块,模块的导出内容可以是变量,也可以是方法;

独立模块:不需要依赖任何其他模块;

导出对象

对象作为参数定义模块,直接作为模块导出;

//moduleA.js
define({printName: printName,getList: function () {return printName;}
});

函数作为参数定义模块,该函数必须有返回值;

函数返回对象:

//moduleA.js
define(function () {return {printName: printName,getList: function () {return printName;}};
});
导出函数

函数作为参数定义模块,该函数必须有返回值;

函数返回函数:

define(function () {//moduleA.jsreturn function () {return printName;};
});
导出符合 CommonJs 规范的接口

非独立模块:需要依赖任何其他模块

define([moduleName], function (require, exports, module, moduleName) {const a = require("a");const b = require("b");exports.action = function () {};//exports={}直接赋值方式会失去与module.export的联系,因此该方式不可取module.exports = {name: 10,action: function () {console.log("输出内容");}};
});

依赖 require, exports, module 的引入可以被省略

exports 直接赋值,该操作会切断被赋值的 exports 与 module.exports 的联系,因此不可直接赋值!!! module.exports 可直接赋值

导入模块

require 导入方式
//使用模块 异步加载
require(["moduleA"], function (moduleA) {moduleA.printName();
});
define 导入方式
//引入依赖模块moduleA
//moduleA的输出值作为函数的参数
define(["moduleA", "jquery", "Base64"], function (moduleA, $, Base64) {return {printName};
});

CMD 规范

CMD,Common Module Definition 规范与 AMD 规范非常相似,但是相对来说更严格。CMD 规范定义的模块在 AMD 中也是合法的,它的基本语法也有点类似于 CommonJS,因此CMD兼容AMD与CommonJs

AMD 规范所推崇的是依赖前置,即提前声明好需要依赖的模块,CMD 推崇就近依赖,一般不在 define 中写依赖,而是使用 require 引入依赖

定义模块

define(id, [dependencies], factory);
  • id:可选,即模块名,该参数省略情况下自动使用文件名,CMD 推崇一个文件一个模块
  • dependencies:依赖列表,CMD 推崇就近依赖,一般不在 define 中写依赖
  • factory:回调函数
    function(require,exports,module){}
    • require 用来获取其他模块提供的接口
    • exports 用来向外提供模块接口
    • module 存储模块相关联的一些属性和方法

导出模块(变量或方法)

exports 是指向 module.exports 的变量。

module 即模块对象,同 CommonJS。

define(function (require, exports, module) {var $ = require("jquery.js");$("div").addClass("active");// module.id == 'module2';//exports.name=$ 对外导出接口
});

注意,与 AMD 一样,尽管 CMD 也使用 require 导入模块,但它也是异步加载的。CMD 规范也会提前扫描回调函数,预下载依赖的模块,然后才会执行回调函数,以防止代码在执行过程中被阻塞

导入模块

require 导入其它模块

UMD 规范

UMD,Universal Module Define,通用模块定义。它是一种适配 CommonJS、AMD 和 CMD 的模块定义标准

UMD 规范在主函数中会通过检查 define、exports 这两个变量,来判断当前使用的模块化规范

!(function (root, name, factory) {// 是否在AMD规范下if (typeof define === "function" && define.amd) {define(name, factory);// 是否在CommonJS或CMD规范下} else if (typeof exports === "object") {module.exports = factory();// 没有使用模块化} else {root[name] = factory();}
})(this, "run", function () {function run() {return "this is run function!";}return run;
});

且 UMD 规范无论是在 CommonJS、AMD 还是 CMD 规范下运行该代码,都可以输出符合所用模块规范的模块,用以判断当前的规范,从而实现了代码的兼容性

UMD 没有对 ES6 的模块规范进行处理,因为 ES6 模块规范是语言层面的规范,未来会得到浏览器的原生支持。一旦 ES6 规范得到原生支持,UMD 规范以及另外三个规范都可能被取代

ES6 规范

传统的模块模式基于闭包,ES6 的模块规范是 JavaScript 语言层面的模块规范,目标是取代上述所有规范,成为前端领域的标准模块规范。未来的浏览器将原生支持该模块规范,也是未来推荐的开发语言

特点

  1. 输出值引用

ES 模块化规范中导出的值是引用,所以不论何时修改模块中的变量,在外部都会有体现。

  1. 静态化

ES6 模块规范的一大特点是静态化,编译的时候就确定模块之间的关系,每个模块的输入和输出变量也是确定的。静态化是为了实现 Tree Shaking 提升运行性能。

Tree Shaking:减少 web 项目中 js 的无用代码,以达到减少用户打开页面的等待的时间,缩短渲染的时间,提升响应的用户体验。

DCE:减少无用代码的操作叫做 DCE(Dead Code Elemination),DCE 意味着更小的体积,缩减 bundle size,从而获得更好的用户体验

ES6 模块化导出有 export 和 export default,建议用 export,因为 export default 导出整体对象,不利于 Tree Shaking,export default 导出的结果可以随意命名,不利于代码的管理

局限性

  1. import 依赖必须在文件的顶部
  2. export 导出变量类型严格限制
  3. import 依赖不能动态确定

导出与导入模块

通过 export 暴露变量与方法;

通过 import 来引入其它模块暴露的值;

const a = 1;
const fn = () => {};
export { a, fn };import { a, fn } from "./index"; //""内是js文件路径

规范的实践案例

主流的开发语言使用的方案

规范说明
CommonJS 规范Nodejs、BrowerifyCommonJS 规范的实践
AMD 规范RequireJSAMD 规范的最好实现
CMD 规范SeaJSCMD 规范的实践

目前,库陆续开发出了ES6版本,但是仍旧存在很多使用以前规范的库版本

规范的混合使用

符合 ES6 规范的模块,简称 ESM,这种是未来的发展趋势,所以很多库的开发都在朝着 ES6 的方向靠拢;如果开发新的项目,也推荐使用ES6规范

Node.js 则是使用的 CommonJS 模块,简称 CJS。

这两种模块不兼容。

那么在 Nodejs 如何加载或运行使用 ESM 呢?

看到网上有提供说是在 package 文件夹中添加 type:"module"的值,因为本人本地测试 node api 服务,与 vue web 项目是在一个目录下,而如果设置 vue 中存在上述 type:"module"属性,会导致 vue 项目启动失败

发现可以将文件后缀名改为.mjs,文件将使用ESM规范解析,js 文件 node 会以 type 的设置值解读。

具体的混合使用的方法可以查看ES6与CJS的混合开发

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

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

相关文章

自媒体新手如何写出爆款公众号文章

今天跟大家分享一下,作为新手怎么样写出一篇阅读量过万的公众号的文章。 我的公众号是从2020年开始写的,写到今天差不多三年多一点。然后现在的粉丝数虽然不多,但也差不多近两千个了。 我这三年多差不多更新了150篇原创文章。刚开始的时候写的…

Nature Commun.:物理所揭示原子分辨下的铁电涡旋畴的原位力学转变过程

通过复杂的晶格-电荷相互作用形成的铁电涡旋畴在纳米电子器件研发中具有巨大的应用潜力。实际应用中,如何在外界激励下操纵这类结构的拓扑状态是至关重要的。中国科学院物理研究所/北京凝聚态物理国家研究中心表面物理国家重点实验室与北京大学、湘潭大学和美国宾夕…

Jwt工具类

导入依赖 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version> </dependency> <dependency><groupId>javax.xml.bind</groupId><artifactId>jax…

关于时区处理策略

前端会通过 App-Id 请求头附带 客户端时区 信息 前端传入的如果是 字符串&#xff0c;会自动根据 请求的客户端时区 解析为对应的 日期 如果前端传入的是时间戳&#xff0c;则无需额外解析转换 如果是 商户后台、管理后台 都统一基于 商户所在国家的时区&#xff08;总台目前…

设计模式之-6大设计原则简单易懂的理解以及它们的适用场景和代码示列

文章目录 前言一、什么是6大设计原则&#xff1f;以及它们的使用场景&#xff1f;1.单一职责原则 (Single Responsibility Principle, SRP)&#xff1a;3.依赖倒置原则 (Dependency Inversion Principle, DIP)&#xff1a;4.接口隔离原则 (Interface Segregation Principle, IS…

文化旅游数据合集,多个数据分享~有图有数据

本周我们将分享一类数据分析常用的数据类型——文化旅游&#xff0c;这里将我们目前所上线的文化旅游类相关数据进行汇总&#xff0c;后续也会持续更新~ 1、全国博物馆数据 基本信息. 数据名称: 全国博物馆数据 数据格式: shpexcel 数据几何类型: 点 数据坐标系: WGS84 …

【MYSQL】MYSQL 的学习教程(五)之 MySQL 索引底层:B+ 树详解

1. 树 树跟数组、链表、堆栈一样&#xff0c;是一种数据结构。它由有限个节点&#xff0c;组成具有层次关系的集合。因为它看起来像一棵树&#xff0c;所以得其名。一颗普通的树如下: 关于树的概念&#xff1a; 结点的度&#xff1a;一个结点含有的子结点个数称为该结点的度树…

C/C++ 块作用域的静态变量static的应用

块作用域的静态变量 静态变量(static variable)听起来自相矛盾&#xff0c;像是一个不可变的变量。实际上&#xff0c;静态的意思是该变量在内存中原地不动&#xff0c;并不是说它的值不变。具有文件作用域的变量自动具有&#xff08;也必须是&#xff09;静态存储器。创建的具…

小红书kos和kop有什么区别,营销玩法有哪些

相信熟悉媒介传播的朋友&#xff0c;对于kol和koc都不陌生。但随着平台的发展和市场的进步&#xff0c;又出现了kos和kop。那么小红书kos和kop有什么区别&#xff0c;营销玩法有哪些&#xff1f; 一、什么是kos和kop KOS&#xff0c;全称叫做Key Opinion Sales&#xff0c;意思…

数据恢复工具推荐!这3款堪称删除文件恢复大师!

“快看看我&#xff01;经常都会莫名奇妙丢失各种电脑文件&#xff0c;但是又无法通过简单的方法找回重要的数据&#xff0c;有没有什么简单的操作可以帮助我快速恢复数据的呀&#xff1f;非常感谢&#xff01;” 在我们的日常生活中&#xff0c;无论是工作还是学习&#xff0c…

搭载紫光展锐芯的移远通信RedCap模组顺利通过中国联通OPENLAB实验室认证

近日&#xff0c;移远通信联合紫光展锐在中国联通5G物联网OPENLAB开放实验室&#xff0c;完成了RedCap模组RG207U-CN端到端测试验收&#xff0c;并获颁认证证书。移远通信RG207U-CN成为业内率先通过联通OPENLAB认证的紫光展锐RedCap芯片平台的模组。 本次测试基于联通OPENLAB实…

千呼万唤始出来!《繁花》定档央八

各位久等&#xff01; 冰雪消融&#xff0c;繁花倾情绽放 央八好戏&#xff0c;即将开场 12月27日19:30起 电视剧《繁花》 登陆CCTV-8黄金强档 接下来 随宝总一起回到九十年代的上海 上世纪九十年代初 煌煌大时代&#xff0c;人人争上游 青年阿宝拜商界高手爷叔为师 左…

java中将Map集合、对象、字符串转换为JSON对象

1、Map集合转JSON对象 创建一个Map集合&#xff1b; 新建json对象&#xff0c;并将Map引入json中。 public void demo1(){ //创建一个Map集合Map<String, String> map new HashMap<>();map.put("1729210001","zhangsan");map.put("17292…

paddle 53 基于PaddleClas2.5训练自己的数据(训练|验证|推理|c++ 部署)

项目地址:https://github.com/PaddlePaddle/PaddleClas 文档地址:https://paddleclas.readthedocs.io/zh-cn/latest/tutorials/install.html paddleclas的最新项目已经不适应其官网的使用案例(训练、验证、推理命令均不适用),为此博主对其进行命令重新进行修改。同时padd…

山海鲸开发者解读智慧电力

作为山海鲸可视化软件的开发者&#xff0c;我们深知可视化技术在智慧电力领域的重要性。在这个能源紧缺、环保意识日益增强的时代&#xff0c;在开发免费好用的可视化软件同时&#xff0c;我们也希望通过数字孪生技术为智慧电力领域提供高效、智能的解决方案&#xff0c;推动电…

若依框架跑起来,Java小白入门(一)

背景 本人Java小白&#xff0c;有一点编程基础&#xff08;c#&#xff09;。企业数字化建设大环境是JAVA&#xff0c;所以需要搞起来&#xff0c;而学习最快的方式就是读代码&#xff0c;学以致用干项目。所以这个系列就是从小白看能否变成小黑。码云上有很多框架&#xff0c;…

剑指Offer 队列栈题目集合

目录 用两个栈实现队列 用两个栈实现队列 刷题链接&#xff1a; https://www.nowcoder.com/practice/54275ddae22f475981afa2244dd448c6 题目描述 思路一&#xff1a; 使用两个栈来实现队列的功能。栈 1 用于存储入队的元素&#xff0c;而栈 2 用于存储出队的元素。 1.push…

Java|IDEA 中添加编译参数 --add-exports

方法1 File > Settings > Build, Execution, Deployment > Compiler > Java Compiler > Javac Options > Override compiler parameters per-module 点击&#xff1a; 点击OK 双击Compliation options&#xff0c;输入后回车&#xff1a; 方法2 找到出错…

KubeSphere应用【五】发布镜像至Harbor

一、IDEA发布镜像至Docker 1.1IDEA安装Docker插件 1.2配置Docker服务器地址 1.3编写POM.XML文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/20…

Tg2520smn ((tcxo / vc-tcxo)高稳定性)

TG2520SMN是一款高稳定性的(TCXO/VC-TCXO)产品&#xff0c;其频率输出范围为10MHz至55MHz&#xff0c;提供多种电源电压选项&#xff0c;分别为1.8 V类型、2.8 V类型、3.0 V类型和3.3 V类型&#xff0c;该产品具备优异的频率/温度特性&#xff0c;最大的频率稳定性为0.5 10^6 …