【前端模块化】JS模块化思想以及相关规范(CommonJS、ES module)

这篇具有很好参考价值的文章主要介绍了【前端模块化】JS模块化思想以及相关规范(CommonJS、ES module)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

JS模块化以及相关规范

1.模块化概念

随着前端应用日趋复杂,项目代码也大量膨胀,模块化就是一种最主流的代码组织方式,一个模块就是一个实现特定功能的文件,它通过把我们的复杂代码按照功能的不同,划分为不同的模块单独维护的这种方式,去提高我们的开发效率,降低维护成本。要用什么功能就加载什么模块,模块化开发是当下最重要的前端开发范式之一,其只是思想,不包含具体实现。

2.模块化开发的优点

  • 避免变量污染、命名冲突等问题
  • 提高代码复用率
  • 提高可维护性
  • 能够进行依赖关系的管理

3.模块化演变过程

1.文件划分方式

将每个功能和相关的一些状态数据单独存放在不同的文件当中,此时一个文件就是一个独立的模块

然后将这个模块引入页面当中,直接调用模块中的成员(变量/函数),一个script标签就对应一个模块,所有模块都在全局范围内工作

例:

html文件:

 <script type='text/javascript' src='module1.js'></script>	<!-- 模块1 -->
 <script type='text/javascript' src='module2.js'></script>	<!-- 模块2 -->
 <script type='text/javascript' src='module3.js'></script>	<!-- 模块3 -->
  <script type='text/javascript'>
    foo();
    bar();
    msg='NBA';   //会污染全局变量
    foo();
  </script>

js文件:

/**
 * 全局函数模式: 将不同的功能封装成不同的全局函数
 * 问题: Global被污染了, 很容易引起命名冲突
 */

let msg = 'modulel'
function foo() {
    console.log('foo()', msg);
}

function bar() {
    console.log('bar()', msg);
}

这种方式的缺点很明显,即:

  • 各模块内部的成员都处在全局作用域中,即任意位置均可进行访问和修改,这样就会污染全局作用域
  • 容易出现命名冲突
  • 无法很好地管理各模块之间的依赖关系。
2.命名空间(namespace)方式

命名空间方式是指:在文件划分方式的基础上,约定每个模块只暴露一个对象,并将该模块中的所有成员封装在该对象中。当需要使用的时候,就调用这个对象的属性即可。

例如:

module_a.js

let moduleA = {
  name: '一碗周',
  handle() {
    console.log(this.name)
  },
}

module_b.js

let moduleB = {
  name: '一碗粥',
  handle() {
    console.log(this.name)
  },
}

html文件:

<body>
  <script src="./component/module_a.js"></script>
  <script src="./component/module_b.js"></script>
  <script>
    console.log(moduleA.name);
    console.log(moduleB.name);	//仍然可以访问到模块中的所有属性
    moduleA.handle()
    moduleB.handle()
  </script>
</body>

所以,这种方法实际上就是简单的对象封装。

这种方式减少了命名冲突的可能,但是各模块中仍然没有私有空间,而且也没有解决管理模块依赖关系的问题。

3.IIFE(立即执行函数)

所谓的IIFE模式就是使用立即执行函数去创建闭包,这种方式为模块提供了私有空间

具体的做法就是:

将模块中每一个成员都放在一个函数提供的私有作用域当中,对于需要暴露给外部的成员可以通过挂载到全局对象上的方式去实现。

这种方式实现了私有成员的概念,就是说模块的私有成员只能在模块内部通过闭包的方式去访问。而在外部,是没有办法去使用的。这样就确保了私有成员的安全。

例:

module_a.js

(function () {
  let name = '一碗周'
  function handle() {
    console.log(name)
  }
  window.moduleA = { handle }	//向window暴露handle对象,从而形成闭包
})()

module_b.js

(function () {
  let name = '一碗粥'
  function handle() {
    console.log(name)
  }
  window.moduleB = { handle }
})()

html文件如下:

<body>
  <script src="./component/module_a.js"></script>
  <script src="./component/module_b.js"></script>
  <script>
    console.log(moduleA.name) // undefined
    console.log(moduleB.name) // undefined,说明无法访问到这个属性,即实现了私有变量的效果
    moduleA.handle()
    moduleB.handle()
  </script>
</body>

在这一阶段,实现了私有成员的概念,但仍未解决模块间的依赖关系问题。

4.IIFE模式增强

这一阶段,在 IIFE 模式的基础上,通过为立即执行函数添加参数的形式,实现模块间的依赖。

例如:

module_a.js

(function () {
  function printName(name) {
    console.log(name)
  }
  // 暴露一个打印的方法
  window.moduleA = { printName }
})()

module_b.js

(function (m) /* 形参 */ {
  let name = '一碗周'
  function sayName() {
    // 使用其他模块的成员
    m.printName(name)
  }
  window.moduleB = { sayName }
})(moduleA) // 实参

html文件:

<body>
  <script src="./component/module_a.js"></script>
  <script src="./component/module_b.js"></script>
  <script>
    moduleB.sayName() // 一碗周,即实现了模块之间的依赖
  </script>
</body>

但这种方式仍然存在问题:

  • 引入了过多 <script> 标签,就需要发送多个请求,请求数量太多
  • 依赖关系模糊
  • 难以维护

下面来介绍两种现在开发过程中常使用的模块化规范:

4.常用模块化规范 — CommonJS

CommonJS在Node.js中广泛应用,Node.js是CommonJS的实践者。

CommonJS规范指出一个单独的文件就是一个模块,它采用的是同步加载模块,也就是说模块加载的顺序就是代码中编写的顺序是一致的,而加载的文件资源大多数都存储在服务器中,所以说加载速度没有什么问题。

但是这种方案不适用与浏览器端,由于网络原因,更合理的方案是采用异步加载(CMD、AMD和ESmodule)

4.1 CommonJS的基本语法

暴露模块使用module.exports,或者直接使用exports,引入模块直接使用require()方法,示例代码如下:

module_c.js

let name = '一碗周'
module.exports = {
  name,
  getName() {
    return name
  },
  setName(n) {
    name = n
  },
}

index.js

// 引入自定义的模块
const person = require('./module_c')
// 引入 Node.js 提供的模块
const fs = require('fs')

console.log(person.getName()) // 一碗周
person.setName('一碗粥')
console.log(person.name) // 一碗周

console.log(person.getName()) // 一碗粥
4.2 CommonJS的模块加载机制

在上面的代码中,首先通过 module.exports 导出一个对象,其中包含一个属性两个方法。然后在index.js中引入该模块,通过require()方法引入模块并定义一个变量来接收这个模块。

但需要注意的是,CommonJS的模块加载机制是被输出值的拷贝 ,也就是说一旦输出了某个值,即使模块内的数据变化,也不会影响这个值了!

上面的代码中通过setName()重新为name进行赋值,在赋值后拿到的结果还是初始值,这是因为name是一个原始类型的值,它的值会被缓存

当我们通过getName()方法来方法name的值才可以获取到没有缓存的那个结果。

5.AMD和CMD

AMD是"Asynchronous Module Definition "的缩写。AMD规范的最佳实践者是require.js。

CMD规范是在sea.js推广中形成的,与AMD类似,不同点在于:AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。

这里不对这两者做介绍…

6.ES Module

6.1 ES Module 的语法特性

如果想要在HTML中使用使用ES Module的话,需要为<script>标签添加一个type="module"的属性,然后就可以执行其中的JS代码。

ES Module有主要以下几个特性:

  • 自动全部采用严格模式,自动忽略'use strict'
  • 每个ES Module都会运行在单独的私有作用域
  • ES Module是通过CORS的方式请求外部JavaScript模块的
  • ES Module的<script>标签会自动延迟执行脚本,相当于加了defer属性,网页对默认的<script>标签采用的是立即执行的机制,页面的渲染会等待这个脚本执行完成才会往下渲染
6.2 ES Module 的导入和导出

导出成员可以通过export导出具体成员,也可以通过export default导出默认成员,示例代码如下:

module_e.js

// 导出单个成员
export let name = '一碗周'
// 导出默认成员
export default function sayMe() {
  console.log('一碗周')
}
// 批量导出成员
// export { name, sayMe }

值得注意的是,批量导出成员的写法并不是导出为一个对象,而是固定的语法,导出得到的是多个成员,导出多个成员必须使用花括号包裹!

想要导出对象,可以使用默认语法,示例代码如下:

export default { name, sayMe }

这样获得的就是一个对象,其中有两个属性。

要注意的是:使用ES Module导出成员,导出的是值的引用 ,也就是说如果模块内部的成员发生改变,所有引用该模块的地方都会发生改变。

导入成员使用import关键字导入,如下代码展示了如何导入一个ES Module模块,示例代码如下:

// 导入默认成员
// import sayMe from './module_e.js'
// 或者通过 as 关键字对导入的默认成员进行重命名
// import { default as sayMe } from './module_e.js'
// 导入指定成员
// import { name } from './module_e.js'

// 也可以将上面两行合并为1行,示例代码如下:
// import { default as sayMe, name } from './module_e.js'
// 或者简写如下:
import sayMe, { name } from './module_e.js'

sayMe()
console.log(name)

但是,我们无法修改导入的成员的值,如果修改则会抛出异常!

import sayMe, { name } from './module_e.js'

name = '1'

异常信息为Uncaught TypeError: Assignment to constant variable.

如果我们只想要执行某个模块,并不需要模块内部的成员,可以直接通过import关键字引入即可。

如果我们想要动态的引入某个成员,可以将import()当做一个函数来使用,示例代码如下:

import('./module.js').then(res=>{
  // res 表示模块的默认导出成员
})

我们可以将导入的模块直接导出,示例代码如下:

export { name } from './module.js'

总结

总的来说,如今模块化已经成为了前端开发者的必备技能了。文章来源地址https://www.toymoban.com/news/detail-790809.html

到了这里,关于【前端模块化】JS模块化思想以及相关规范(CommonJS、ES module)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 模块化规范

    常用模块化有两种规范,commonJS和ES6 我们常遇到的使用场景是,在commonJS的模块里需要引入ES6规范的模块。这时就需要把ES6模块转译为commonJS规范的模块,否则报错 转义工具有: Babel:Babel 是一个流行的 JavaScript 编译器,它可以将 ES6 模块转译为 CommonJS 模块。你可以使用 Bab

    2024年02月15日
    浏览(43)
  • 前端框架前置课Node.js学习(1) fs,path,模块化,CommonJS标准,ECMAScript标准,包

    目录 什么是Node.js 定义 作用: 什么是前端工程化 Node.js为何能执行Js fs模块-读写文件 模块 语法: 1.加载fs模块对象 2.写入文件内容 3.读取文件内容 Path模块-路径处理 为什么要使用path模块 语法 URL中的端口号 http模块-创建Web服务 需求 步骤: 案例:浏览时钟 步骤: 什么是模块化 定

    2024年01月16日
    浏览(110)
  • PCB模块化设计05——晶体晶振PCB布局布线设计规范

    1、布局整体紧凑,一般放置在主控的同一侧,靠近主控IC。 2、布局是尽量使电容分支要短(目的:减小寄生电容,) 3、晶振电路一般采用π型滤波形式,放置在晶振的前面。 1)走线采取类差分走线; 2)晶体走线需加粗处理:8-12mil,晶振按照普通单端阻抗线走线即可;

    2024年02月12日
    浏览(52)
  • 前端10年进化 Node.js、模块化、CommonJS、AMD、CMD、Webpack、Vue-cli、Electron-vue

    模块化的概念在软件开发领域已经存在很长时间,但在 JavaScript 中的模块化发展相对较晚。以下是对您提出的问题的回答: 提出时间:JavaScript 中的模块化概念相对较早地提出于 CommonJS 规范。CommonJS 是一种 JavaScript 模块化规范,最早在 2009 年由 Ryan Dahl 和其他社区成

    2024年02月11日
    浏览(79)
  • TS编译器选项——指定编译ES版本和模块化使用规范

    compilerOptions是TS的编译器选项,主要在tsconfig.json文件中用于对ts编译为js文件时进行配置 \\\"compilerOptions\\\" : { 配置项 } 版本可以为如下版本:\\\'es3\\\', \\\'es5\\\', \\\'es6\\\', \\\'es2015\\\', \\\'es2016\\\', \\\'es2017\\\', \\\'es2018\\\', \\\'es2019\\\', \\\'es2020\\\', \\\'es2021\\\', \\\'es2022\\\', \\\'esnext\\\'. 版本可以为如下版本:\\\'none\\\', \\\'commonjs\\\', \\\'amd\\\', \\\'system\\\', \\\'u

    2024年02月04日
    浏览(53)
  • PCB模块化设计09——RJ45-以太网口PCB布局布线设计规范

    以太网(Ethernet)是一种计算机局域网组网技术,该技术基于IEEE制定的IEEE 802.3标准,它规定了包括物理层的连线、电信号和介质访问层协议的内容。 以太网是当前应用最普遍的局域网技术。Ethernet的接口是实质是MAC通过MII总线控制PHY的过程。 以太网接口电路主要由MAC控制器

    2024年02月10日
    浏览(53)
  • js模块化开发

    ◼ 到底什么是模块化、模块化开发呢?  事实上模块化开发最终的目的是将程序划分成一个个小的结构;  这个结构中编写属于自己的逻辑代码,有自己的作用域,定义变量名词时不会影响到其他的结构;  这个结构可以将自己希望暴露的变量、函数、对象等导出给其结

    2024年02月13日
    浏览(61)
  • JS模块化系统

    随着 JavaScript 开发变得越来越广泛,命名空间和依赖关系变得越来越难以处理。人们已经开发出不同的解决方案以模块系统的形式来解决这个问题。 CommonJS 是一种同步加载模块的规范,主要用于服务器端的 Node.js 环境。 top:CommonJS 加载的是一个对象(即 module.exports 属性),

    2024年02月19日
    浏览(61)
  • js 模块化

    模块化主要是用来抽离公共代码,隔离作用域,避免变量冲突等。 模块化的整个发展历史如下: IIFE :使用自执行函数来编写模块化,特点:在一个单独的函数作用域中执行代码,避免代码冲突。 AMD :使用 require 来编写模块化,特点:依赖必须提前声明好。 CMD :使用 seaJS

    2024年02月14日
    浏览(51)
  • 前端模块化

      随着前端应用的日益复杂,我们的项目代码已经逐渐膨胀到了不得不花大量时间去管理的程度了。而模块化就是一种最主流的代码组织方式,它通过把复杂的代码按照功能的不同划分为不同的模块单独维护,从而提高开发效率、降低维护成本。模块化可以使你能够更容易地

    2024年02月08日
    浏览(66)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包