模块化开发 - CJS、AMD、CMD、ESM

本文介绍了前端模块化从IIFE到CommonJS、AMD、CMD、ESM的演进过程,详述了各规范的特点和优缺点。CJS适用于服务器端,同步加载;AMD(如require.js)支持异步加载;CMD(如sea.js)按需执行;ESM成为现代浏览器的默认标准,支持动态加载。UMD则用于兼容不同环境。
摘要由CSDN通过智能技术生成

简述模块发展史

凑合的模块系统

利用立即调用函数表达式 (Immediately Invoked Function Expresssion 简称 IIFE)将代码块封装在匿名闭包中,代码块是立即执行的。

// iifeModule是为匿名函数代码块创建的命名空间
var iifeModule = (function () {
    var count = 0
    return {
        increase: function(){
            ++count
        },
        reset: function(){ 
            count = 0 
        }
    }
})()
iifeModule.increase()
iifeModule.reset()

通过传参,使代码块可以有额外的依赖 。

var deps1 = function () {
    console.log('deps1')
}
var deps2 = function () {
    console.log('deps2')
}
var iifeModule = (function (deps1, deps2) {
    var count = 0
    deps1()
    deps2()
    return {
        increase: function(){
            ++count
        },
        reset: function(){ 
            count = 0 
        }
    }
})(deps1, deps2)
iifeModule.increase()
iifeModule.reset()

传统框架还应用了揭示模块模式(revealing module pattern)。这种模式只返回一个对象,属性值是私有成员的数据和引用,使用者无需关注底层实现。

var revealingModule = (function (deps1, deps2) {
    var count = 0
    deps1()
    deps2()
    var publicIncrease = function(){ ++count }
    var publicReset = function(){ count = 0 }
    return { increase: publicIncrease, reset: publicReset }
})(deps1, deps2)

在模块内部也可以定义模块,这样可以实现命名空间嵌套:

var revealingModule = (function (deps1, deps2) {
    var count = 0
    deps1()
    deps2()
    var publicIncrease = function(){ ++count }
    var publicReset = function(){ count = 0 }
    // 这样可以实现命名空间嵌套
    var publicCount = {
            getCount: function (){
                return count
            }
        }
    return { increase: publicIncrease, reset: publicReset, count: publicCount }
})(deps1, deps2)
revealingModule.count.getCount()

但是如果模块间存在依赖,引入文件时会有严格的顺序,否则会产生运行时bug。

随着前端工程的日益庞大,前端模块化也经过了漫长的发展。模块化的基本思想:把逻辑分块,各自封装,相互独立,每个块自行决定对外暴露什么,同时自行决定引入执行哪些外部代码。不同的实现和特性,产生了不同的规范。

成熟的模块系统

目前主流的模块化规范是CJS、AMD、CMD、ESM。

CJS -- CommonJS   node.js 指定的标准  服务器端模块规范

// easyExport.js 文件
// 导出
let easyExport = function easyExport(){}
module.exports = { easyExport }
// exports.easyExport = easyExport

/************************************/
// core.js 文件
// 导入
let easyExport = require('./easyExport.js')

通过 module + exports 去对外暴露接口。

通过 require 去调用其他模块。

优点:所有模块同步加载。

缺点:服务器端运行可以,浏览器端就会造成js解析阻塞,页面加载速度缓慢。

AMD -- Asynchronous Module Definition / 异步模块定义

允许定制回调函数 经典实现框架:require.js

通过define定义,使用require一次性引入所有需要的依赖,将依赖前置

// define(id?, [depends]?, callback)
// require([module], callback)

define('amdModule', ['deps1', 'deps2'], (deps1, deps2) => {
    // 业务逻辑
    let count = 0
    const increase = function(){++count}
    deps1()
    deps2()
    return { increase }

})

reuire(['amdModule'], amdModule => {
    amdModule.increase()
})

优点:支持模块异步加载,经典实现框架 require.js

缺点:会有引入成本,没有考虑按需加载;因为是异步加载,加载完之后就执行,可能遇到某个依赖中用到的另一个依赖还没加载到

CMD -- Common Module Definition / 通用模块定义

主要应用框架 sea.js

通过define定义,在需要用到依赖的地方使用require引入,支持按需解析,执行

// math.js
define(function(require, exports, module) {
  exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
      sum += args[i++];
    }
    return sum;
  };
});

// increment.js
define(function(require, exports, module) {
  var add = require('math').add;
  exports.increment = function(val) {
    return add(val, 1);
  };
});

// program.js
define(function(require, exports, module) {
  var inc = require('increment').increment;
  var a = 1;
  inc(a); // 2

  module.id == "program";
});

优点:异步加载,按需执行,依赖就近,用到的依赖都是确定加载并执行完成的

缺点:依赖于打包;扩大了模块内的体积;加载时需先解析模块路径

很多时候工程中会同时支持多种方式,那么需要如何兼容呢,就出现了UMD(Universal Module Definition)。

// UMD
 (function(global, factory) {
    if (typeof module !== 'undefined' && typeof exports === 'object') {
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        define(['moduleName'], factory)
    } else if (typeof define === 'function' && define.cmd) {
        define(function(require, exports, module) {
            module.exports = factory()
        })
    } else {
        global = typeof globalThis !== 'undefined' ? globalThis : global || self 
        global.module = factory();
    }
}(this, function() {
    return {}
}))

ESM -- ES Modules

普通函数定义,通过 export 导出,通过 import 引入

一个文件只能有一个export default ,export可以有多个

// increase.js
let count = 0
export increase = () => { ++count }

// cala.js
import { increase } from 'increase'
const cala = increase()
export default cala

ES11原生支持动态加载
import('deps1').then((deps2) => {
    deps2()
})

优点:通过一种最统一的形态整合了所有JS的模块化

`vue-json-excel.esm.js` 是 Vue 用于将 JSON 数据转换成 Excel 表格的一个插件。如果你想在生成Excel文件前添加水印,这个插件本身并没有提供直接处理图片水印的功能。不过,你可以通过自定义 `beforeGenerate` 方法来自行实现这一功能。 在这个方法中,你可以对生成的每一行数据进行操作,例如在添加到 Excel 文件之前先在其单元格上覆盖一层包含水印的文字或图片。这通常需要你自己编写一些逻辑,比如用 HTML 或者 Canvas 来创建水印并叠加在原始数据上。 以下是一个简单的示例思路: ```javascript export default { data() { return { watermarkText: '这是我的水印', // ... 其他配置项 }; }, methods: { generateWithWatermark(data) { // 创建一个新的工作表对象 const worksheet = this.excel.addWorksheet(); // 遍历数据,为每行添加水印 for (let i = 0; i < data.length; i++) { let row = worksheet.addRow(); // 将原数据插入列 row.values = data[i]; // 添加水印文本 const watermarkRow = row.clone(); watermarkRow.getCell(0).value = this.watermarkText; // 调整水印位置、字体样式等(这里假设你是基于单元格宽度) watermarkRow.cells[0].alignment = { horizontal: 'right', vertical: 'bottom', margin: 5 }; // 将水印行添加到工作表底部 worksheet.addRow(watermarkRow); } // 执行生成操作 this.excel.generate(this.outputFile); }, }, beforeGenerate: function () { // 在这里调用自定义的generateWithWatermark方法 this.generateWithWatermark(this.jsonData); }, // ...其他配置和生命周期钩子 }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值