常用的前端模块化标准梳理

1、模块化标准出现以前使用的模块化方案:

1)文件划分: 将不同的模块定义在不同的文件中,然后使用时通过script标签引入这些文件

缺点:

  • 模块变量相当于是定义在全局的,容易造成变量名冲突(即不同模块存在相同名称的变量);
  • 模块之间的依赖关系和加载顺序不好管理;如果模块之间存在依赖关系,则在引入文件时需要手动调整,否则会出现运行错误
  • 如果引入了多个模块,则无法清楚知道某个变量来自哪个模块,需要逐个模块进行排查;

2)命名空间:

javascript 复制代码
// module.a.js文件
window.moduleA = {
 data: 'A',
 ...
}
// module.b.js文件
window.moduleB = {
 data: 'B',
 ...
}
// html中访问模块变量
...
console.log(moduleA.data)
console.log(moduleB.data)

优点:

  • 解决了文件划分造成的全局变量命名冲突的问题;
  • 使用时能清楚知道某个变量来自哪个模块,方便开发调试

3)IIFE-立即执行函数,使变量私有化,只对外界暴露变量的访问入口

javascript 复制代码
// module.a.js文件
(
  function () {
    // 模块A的私有成员,只能在当前模块内部访问,其他模块无法访问
    const data = 'moduleA'

    function method () {
      console.log(data + ' execute');
    }
    window.moduleA = {
      method
    }
  }
)()

// module.b.js文件
(
  function () {
    const data = 'moduleB'
    function method () {
      console.log(data + ' execute');
    }
    window.moduleB = {
      method
    }
  }
)()

// index.html文件
...
<body>
  <script src="./js/module.a.js"></script>
  <script src="./js/module.b.js"></script>
  <script>
    // moduleA的私有成员,外界无法访问
    console.log(moduleA.data);  // undefined
    moduleB.method()  // moduleB execute
  </script>
</body>

优点:

  • 解决了全局变量命名冲突的问题
  • 能清楚知道使用的变量来自哪个模块,(即变量作用域清晰了)

总结:命令空间和IIFE均没有解决模块存在依赖关系时,模块加载顺序的问题、

2、主流的前端模块规范

1)CommonJs规范:

  • 使用module.exports导出模块;require()导入模块;
javascript 复制代码
// module.a.js文件
const data = 'hello CommonJs'
function getData () {
  return data
}
// 导出模块
module.exports = {
  getData
}

// main.js文件
const { getData } = require('./module.a')
console.log(getData())

特点

  • 同步加载模块;即模块加载是阻塞式的,需等待模块加载完才能执行后续操作;
  • 适用于服务器端;因为模块是在服务器本地无需进行网络IO,另外在服务启动时才会进行模块加载,而通常服务启动后就会一直运行,所以同步加载对服务的性能影响不大;但是在浏览器环境中,同步模块加载会造成浏览器JS解析过程的阻塞,导致页面加载速度缓慢;
  • 模块缓存,避免重复加载和执行

2)AMD模块规范(Asynchronous Module Definition)

javascript 复制代码
// js/main.js文件

// 使用define()加载依赖模块print.js,并定义一个新模块;也可使用require()加载依赖模块
define(['./js/print.js'], function (module) {
  // module:依赖项的导出内容
  module.print('AMD')  // print AMD
})

// js/print.js文件
// 定义一个模块并导出
// define(array, callback): array-依赖项数组;callback-回调函数,接收依赖项的导出内容
define([], function () {
  return {
    print: function (msg) {
      console.log('print ' + msg);
    }
  }
})

// index.html文件
...
<body>
  <!-- 由于AMD没有得到浏览器的原生支持,所以使用AMD规范时,需要引入第三方库,requireJS -->
  <!-- data-main="./js/main":指定主模块路径,标识main.js文件位于js目录下 -->
  <script src="./lib/require.js" data-main="./js/main"></script>
  ...
</body>

特点:

  • 异步加载模块,即模块加载时不会阻塞页面渲染
  • 未得到浏览器的原生支持,需要借助第三方库requireJS来实现
  • 代码书写和阅读稍显复杂,增加了开发人员的心智负担

同期出现的其他规范: CMD,UMD

  • CMD: 由淘宝出品的SeaJs实现,解决的问题与AMD类似;延迟执行和按需加载,即模块是在代码需要使用时才会加载执行,不会预先加载;
  • UMD:通用模块定义,兼容AMD和CommonJs的模块化方案,可以同时运行在浏览器和Node.js环境

3)ES模块(ES Module)

  • 由ECMAScript官方提出的模块化规范,已经得到现代浏览器的内置支持
  • 如果在html的script标签中加上type="module"属性,那么浏览器将按照ES的模块化规范加载和解析该脚本
  • 能够同时运行在浏览器端和Node.js环境中,拥有天然的跨平台能力
  • 使用 export 导出模块;import 导入模块;
javascript 复制代码
// ES模块在浏览器环境中运行
// module.a.js文件
// 使用export导出模块
const method = () => {
  console.log('Hello ES Module')
}
export {
  method
}

// module.b.js文件
// 使用export default导出模块
const method = () => {
  console.log('Hello ES Module')
}
export default {
  method
}

// main.js文件,引用模块
// 1、export语法导出时引入方式,可通过 as xx 给导入的方法起别名
import { method as methodA } from './module.a.js'
methodA()

// 2、export default语法导出时引入方式
import B from './module.b.js'
B.method()

// index.html文件
...
<body>
  <!-- type="module" 在html中标识这个脚本文件是一个ES模块; -->
  <script type="module" src="./js/main.js"></script>
</body>
javascript 复制代码
// ES模块在Node.js环境中运行

// package.json文件,添加type: "module"属性,使Node.js以ES规范解析模块
{
  "type": "module"
   ...
}

优点:

  • 导入,导出语法简洁明了,代码可读性强
  • 静态分析和优化;模块的依赖关系在编译时就可以确定,这使得编译器可以进行更好的静态分析,并进行代码优化,打包和压缩等操作;
  • 模块的按需加载;
  • 可以同时运行在浏览器和Node.js环境

全文参考:

《深入浅出 Vite》

相关推荐
qq_3901617721 分钟前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test1 小时前
js下载excel示例demo
前端·javascript·excel
Yaml41 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事1 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶1 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
getaxiosluo1 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
理想不理想v1 小时前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
知孤云出岫1 小时前
web 渗透学习指南——初学者防入狱篇
前端·网络安全·渗透·web
贩卖纯净水.1 小时前
Chrome调试工具(查看CSS属性)
前端·chrome
栈老师不回家2 小时前
Vue 计算属性和监听器
前端·javascript·vue.js