AMD 模块化最佳实践

更新日期: 2019-07-17阅读: 3.6k标签: 模块化

AMD 是 RequireJS 给出的模块加载方案。 支持递归依赖解析、模块异步加载,夜兼容 CommonJS 可以在 Node.js 里用。 虽然目前已经不再流行,很多站点更倾向于编写 ES Modules 并直接 webpack 打包, 但 AMD 是完整的,兼容性良好的,支持动态加载的模块化方案,在大型的、独立部署的、异构的项目中仍然有一席之地。 所以还是决定写一篇关于 AMD 最佳实践的文章,致敬老式的 Old School 的 Web 开发。

TL;DR

  • 源文件中,模块应该匿名编写。区分好“使用 AMD 语法”和“模块化”,充分利用 AMD 又不被套牢。
  • 一个模块对应一个文件。每个模块是一个单独的源文件,每个文件只包含一个模块定义。
  • 避免手动写依赖列表,可以通过编译工具自动生成。
  • 独立维护的工具模块,应当通过打包编译隐藏其内部结构。


模块匿名:源文件名即模块名

源文件中,模块应该匿名编写。为了理解这一点, 首先要区分“使用 AMD 语法”和“模块化”这两件事情。 模块化是目标而“使用 AMD 语法”只是手段, 我们最终想要的模块代码是被 define 包裹起来的 function 里面的这一部分。 最理想的方式是只编写模块内容,define 在编译时完成。例如:

// 源码:src/foo.js
exports.foo = x => console.log(x)

// 编译后:dist/foo.js
define('foo', function () {
    return { foo: x => console.log(x) }
})

如果希望有更大的自由,可以更容易地和 RequireJS 互操作,也可以在源码中包含 define :

// 源码:src/foo.js
define(function () {
    return { foo: x => console.log(x) }
})

// 编译后:dist/foo.js
define('foo', function () {
    return { foo: x => console.log(x) }
})

注意上述代码块中源码模块是匿名的。也就是说模块本身只包含模块化的业务逻辑, AMD 特定的部分由编译来解决不混入源码。否则会对后续维护造成困难: 文件重命名、移动目录时,如果变更 ID 会使得引用挂掉,如果不变更 ID 又会跟文件名不一致。 从 AMD 迁移到其他模块化方案时也会遇到类似的问题。


一个模块对应一个文件

每个模块是一个单独的源文件,每个文件只包含一个模块定义。如果一个文件包含多个模块,那么势必会编写出具名模块,手动管理所有的 ID 和引用关系。 这违反了上一个实践:模块匿名。

另一个反例是源文件中不仅定义了模块,还在模块外写了其他代码:

// file: foo.js
define('foo', function () { /* do some thing */ })
require(['foo'])

如上 foo.js 就不是一个模块文件,它只是一个 JavaScript 文件, 一个不可复用的,不可测试的 JavaScript 文件。 它是自执行的,不是用来让别人 require 的, 效果上等价于一个 IIFE,因此完全没有必要写成一个 AMD 模块。


自动生成依赖列表

避免手动维护依赖列表,把重复性工作交给编译器。 因为一个模块的依赖可能很多而且是变化的,比如这个:

define(['skyWalker', 'starShipManager', 'theLastJedi', 'theVeryLastJedi',
function (skyWalker, starShipManager, theLastJedi, theVeryLastJedi) {
    // do something    
}])

手动维护一个字符串列表和一个形参列表不仅麻烦还容易出错,而且一旦错位了很难调试。 这些工作完全可以交给编译器,这也是 local require 的重要用法:

define(function (require) {
    var skyWalker = require('skyWalker')
    var starShipManager = require('starShipManager')
    var theLastJedi = require('theLastJedi')
    var theVeryLastJedi = require('theVeryLastJedi')
    // do something    
})

RequireJS 本身也利用 Function.prototype.toString (见 fdf418 ) 提供了依赖分析,上述代码甚至不需要编译就可以在浏览器里运行。 如果再把 define 这一层包装放到浏览器里,你写的就是 CMD 模块了,然后通过编译得到 AMD 规范的模块。


隐藏模块的内部结构

对于一个采用 AMD 方案的,由很多独立维护的模块构成的最终系统。 默认这些独立模块的文件结构会完全映射到最终系统中。 这使得模块之间可以相互引用深层的内部文件,而不只是模块入口。 例如最终模块引用了一个叫做 foo 的 AMD 规范的独立模块:

├── index.js
└── node_modules
    └── foo
        ├── src/
        │   └── bar.js
        └── index.js

最终打包后代码可能是:

define('index', [ 'node_modules/foo/index', 'node_modules/foo/src/bar' ], function (foo, bar) { 
    console.log(foo, bar);
})

其中对 node_modules/foo/src/bar 的引用是脆弱的。 因为 foo 是一个独立维护的模块,其 api 由入口文件 index.js 定义: bar 不应当暴露给外部使用,我们需要技术手段来禁止这种引用操作。比如:

foo
foo

再配合适当的 requirejs 配置,总之需要达到的效果类似:

define('index', [ 'foo' ], function (foo) { 
    console.log(foo, foo.bar);
})

原文:https://harttle.land/2019/07/16/amd-best-practices.html



链接: https://www.fly63.com/article/detial/4228

一览js模块化:从CommonJS到ES6

模块化是指把一个复杂的系统分解到一个一个的模块。模块化开发的优点:代码复用,让我们更方便地进行代码管理、同时也便于后面代码的修改和维护。一个单独的文件就是一个模块,是一个单独的作用域,只向外暴露特定的变量和函数。

JS模块化

CommonJS 是服务器端的模块化方案,nodeJs 就采用了这种方案。在 CommonJS 规范中,一个文件即一个模块,用module.exports和exports定义模块输出的接口,用require加载模块。在 requireJS 中用define定义模块,require载入模块,require.config用来配置路径。ES6 Module 主要使用export输出,import加载。

js模块化总结

在很长的一段前端历史里,是不存在打包这个说法的。那个时候页面基本是纯静态的或者服务端输出的, 没有 AJAX,也没有 jQuery。Google 推出 Gmail 的时候(2004 年),XMLHttpRequest, 也就是我们俗称的 AJAX被拾起的时候

ES6 模块化和 .vue组件的应用举例

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。export通过接口,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例。

Js模块化方案总结

本文包含两部分,第一部分通过简明的描述介绍什么是 CommonJS、AMD、CMD、UMD、ES Module 以及它们的常见用法,第二部分则根据实际问题指出在正常的 webpack 构建过程中该如何指定打包配置中的模块化参数。

css模块化方案

这篇文章主要介绍了css模块化方案,css的模块化方案可能和js的一样多,下面简单介绍几种主要的模块方案,非常具有实用价值,需要的小伙伴可以参考下。css的模块化方案可能和js的一样多,下面简单介绍几种主要的模块方案

理解JS 模块化

在模块化规范形成之前,JS开发者使用Module设计模式来解决JS全局作用域的污染问题。Module模式最初被定义为一种在传统软件工程中为类提供私有和公有封装的方法。在JavaScript中,Module模式使用匿名函数自调用 (闭包)来封装

前端模块化

众所周知,早期 JavaScript 原生并不支持模块化,直到 2015 年,TC39 发布 ES6,其中有一个规范就是 ES modules(为了方便表述,后面统一简称 ESM)。但是在 ES6 规范提出前,就已经存在了一些模块化方案

ES6与 CommonJS 模块化的区别

CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。ES6 Modules 的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。

前端模块化:CommonJS,AMD,CMD,ES6

模块化的开发方式可以提高代码复用率,方便进行代码的管理。通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数。目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!