前言

主要学习一下四种模块加载规范:

  1. AMD
  2. CMD
  3. CommonJS
  4. ES6 模块

历史

前端模块化开发那点历史

require.js

requirejs 为全局添加了 define 函数,你只要按照这种约定的方式书写这个模块即可。

  1. define(function () {
  2. //Do setup work here
  3. return {
  4. color: "black",
  5. size: "unisize"
  6. }
  7. });
  1. //my/shirt.js now has some dependencies, a cart and inventory
  2. //module in the same directory as shirt.js
  3. define(["./cart", "./inventory"], function(cart, inventory) {
  4. //return an object to define the "my/shirt" module.
  5. return {
  6. color: "blue",
  7. size: "large",
  8. addToCart: function() {
  9. inventory.decrement(this);
  10. cart.add(this);
  11. }
  12. }
  13. }
  14. );

以上示例代码来源于require.js官网

demo代码详见 https://github.com/BillyQin/jsModule/tree/master/requireJs

AMD

require.js 为全局添加了define 函数,按照这种约定方式写即可。

这个约定方式就是AMD(The Asyncchronous Module Definition)

所以AMD规范就是定义了怎么写define函数。只要按照这个规范来写模块和依赖,require.js就能正确解析。

sea.js

demo代码详见 https://github.com/BillyQin/jsModule/tree/master/seaJs

CMD

同样的道理,CMD就是Sea.js对模块定义对规范化产出。

所以CMD的内容就是描述该如何定义模块,如何引入模块,如何导出模块。只要按照这个规范来写模块和依赖,sea.js就能正确解析。

AMD 和 CMD

  1. AMD 推崇依赖前置,
  2. CMD推崇依赖就近 

  3. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。
  • AMD 是将需要使用的模块先加载完再执行代码

  • CMD 是在 require 的时候才去加载模块文件,加载完再接着执行。

CommonJS

AMD 和 CMD 都是用于浏览器的模块规范,而在服务端(node),则采用CommonJS。

CommonJS和sea.js一样,require的时候才去加载模块文件,加载完再接着执行。

demo代码详见 https://github.com/BillyQin/jsModule/tree/master/commonJs

为什么浏览器中不支持 CommonJS 语法呢?

这是因为浏览器环境中并没有 module、 exports、 require 等环境变量。

ES6

es6定义了新的模块加载方案。

  1. // 导出
  2. const addr = 'China'
  3. const year = 2018
  4. export { addr, year }
  1. // 导入
  2. import { addr, year } from './index.js'

和require.js(AMD)一致,将需要使用的模块加载完再执行代码。

ES6 和 CommonJS的差异

  1. CommonJS模块输出值的拷贝, ES6输出值的引用。 CommonJS模块输出值的拷贝, 也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

  2. CommonJS是运行时加载,ES6是编译时输出接口。 CommonJS加载的是一个对象,就是module.exports属性。该对象只有在脚本运行完成后才会生成。而es6模块不是对象,对外接口只是一种静态定义,在代码静态解析阶段就会生成。

Babel

es6语法在线转换

在浏览器不支持es6的时候,如果要使用es6的语法,一般都会在项目里加入babel。

  1. // es6
  2. let firstName = 'Michael';
  3. const lastName = 'Jackson';
  4. var year = 1958;
  5. export {firstName, lastName, year};

转换后

  1. Object.defineProperty(exports, "__esModule", {
  2. value: true
  3. });
  4. var firstName = 'Michael';
  5. var lastName = 'Jackson';
  6. var year = 1958;
  7. exports.firstName = firstName;
  8. exports.lastName = lastName;
  9. exports.year = year;

webpack

Babel 只是把 ES6 模块语法转为 CommonJS 模块语法,而浏览器不支持CommonJs。这时候webpack出动。

浏览器不支持CommonJs的本质是因为浏览器环境中并没有 module、 exports、 require 等环境变量。 webpack 打包后的文件之所以在浏览器中能运行,就是靠模拟了这些变量的行为。

webpack怎么模拟呢?

  1. // commonJs
  2. let multiply = require('./multiply')
  3. console.log('加载 square 模块')
  4. let square = function (num) {
  5. return multiply.multiply(num, num)
  6. }
  7. module.exports = {
  8. square: square
  9. }

模拟后:

  1. // 包裹一层,注入这些变量
  2. function(module, exports, require) {
  3. console.log('加载了 square 模块');
  4. var multiply = require("./multiply");
  5. module.exports = {
  6. square: function(num) {
  7. return multiply.multiply(num, num);
  8. }
  9. };
  10. }

整个CommonJs项目改写后

  1. // 自执行函数
  2. (function(modules){
  3. // 存储已加载的模块
  4. var installModules = {}
  5. // 关键的require方法
  6. function require(moduleName) {
  7. if (installModules.moduleName) {
  8. return installModules.moduleName.exports
  9. }
  10. var module = installModules[moduleName] = {
  11. exports: {}
  12. }
  13. modules[moduleName](module, module.exports, require);
  14. return module.exports;
  15. }
  16. return require('main')
  17. })({
  18. 'main': function(module, exports, require) {
  19. var addModule = require("./add");
  20. console.log(addModule.add(1, 1))
  21. var squareModule = require("./square");
  22. console.log(squareModule.square(3));
  23. },
  24. './add': function(module, exports, require) {
  25. console.log('加载 add 模块')
  26. var add = function (x, y) {
  27. return x + y
  28. }
  29. module.exports = {
  30. add: add
  31. }
  32. },
  33. './multiply': function(module, exports, require) {
  34. console.log('加载 multiply 模块')
  35. var multiply = function (x, y) {
  36. return x * y
  37. }
  38. module.exports = {
  39. multiply: multiply
  40. }
  41. },
  42. './square': function(module, exports, require) {
  43. console.log('加载 square 模块')
  44. var multiply = require('./multiply')
  45. var square = function (num) {
  46. return multiply.multiply(num, num)
  47. }
  48. module.exports = {
  49. square: square
  50. }
  51. }
  52. })

参考

javascript之模块加载方案的更多相关文章

  1. ECMA Script 6_模块加载方案 ES6 Module 模块语法_import_export

    1. 模块加载方案 commonJS 背景: 历史上,JavaScript 一直没有模块(module)体系, 无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来. 其他语言都有这项功能: ...

  2. javascript 异步模块加载 简易实现

    在javascript是没有类似java或其他语言的模块概念的,因此也不可能通过import或using等关键字来引用模块,这样造成了复杂项目中前端代码混乱,变量互相影响等. 因此在复杂项目中引入AM ...

  3. JavaScript AMD 模块加载器原理与实现

    关于前端模块化,玉伯在其博文 前端模块化开发的价值 中有论述,有兴趣的同学可以去阅读一下. 1. 模块加载器 模块加载器目前比较流行的有 Requirejs 和 Seajs.前者遵循 AMD规范,后者 ...

  4. [JavaScript] 前端模块加载简单实现(require)

    模块加载的简单实现 (function(win) { var baseUrl; var paths; var script_cache = {}; var script_queue = []; var ...

  5. requirejs解决异步模块加载方案

    他首先会遍历enableRegistry取出其中定义的模块,并且将没有加载成功的模块标识注入noLoads数组,如果过期了这里就会报错 如果上述没问题还会做循环依赖的判断,主要逻辑在breakCycl ...

  6. 关于javascript模块加载技术的一些思考

    前不久有个网友问我在前端使用requireJs和seajs的问题,我当时问他你们公司以前有没有自己编写的javascript库,或者javascript框架,他的回答是什么都没有,他只是听说像requ ...

  7. ES6模块加载

    两种加载方式 加载方式 规范 命令 特点 运行时加载 CommonJS/AMD require 社区方案,提供了服务器/浏览器的模块加载方案 非语言层面的标准 只能在运行时确定模块的依赖关系及输入/输 ...

  8. 关于前端JS模块加载器实现的一些细节

    最近工作需要,实现一个特定环境的模块加载方案,实现过程中有一些技术细节不解,便参考 了一些项目的api设计约定与实现,记录下来备忘. 本文不探讨为什么实现模块化,以及模块化相关的规范,直接考虑一些技术 ...

  9. 小矮人Javascript模块加载器

    https://github.com/miniflycn/webkit-dwarf 短小精悍的webkit浏览器Javascript模块加载器 Why 我们有许多仅基于webkit浏览器开发的应用 无 ...

随机推荐

  1. 3.用Redis Desktop Manager连接Redis(CentOS)

    Redis Desktop Manager是Redis图形化管理工具,方便管理人员更方便直观地管理Redis数据. 然而在使用Redis Desktop Manager之前,有几个要素需要注意: 一. ...

  2. HDU_1028_Ignatius and the Princess III_(母函数,dp)

    Ignatius and the Princess III Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K ...

  3. 在Excel表格中打字为何会被覆盖掉

    修改证件图片背景:https://www.cnblogs.com/liyanbin/p/9466746.html Insert键是插入和改写(覆盖)的开关如果当前处于改写(覆盖)模式,再按一下Inse ...

  4. SSHFS使用笔记

    在写树莓派集群项目的时候,发现如果在树莓派上维护的代码需要非常费力才能跟本地项目代码同步,因此打算将Server端和Client端代码分开,树莓派上的Client端代码远程挂载到本地,这样做比之前要更 ...

  5. THUSC2019 退役记

    Day -inf 这一个半月潜心搞文化课,把文化课的坑填上了不少,我文化课的底子真是薄啊 一年前没想过我还挺有希望进队的,最后还差点冲上 一年后说不定会发现我搞文化课也能搞得不错呢? 一切都是未知 t ...

  6. 51.percentiles rank以及网站访问时延SLA统计

    主要知识点: percentile_ranks的用法 percentile的优化     一.percentile_ranks的用法 SLA:就是所提供的服务的标准. 比如一个网站的提供的访问延时的S ...

  7. 03.requests模块(1)

    目录 03.requests模块(1) 展开requests模块的学习 代码实例 需求:爬取搜狗指定词条搜索后的页面数据 需求:登录豆瓣电影,爬取登录成功后的页面数据 需求:爬取豆瓣电影分类排行榜 h ...

  8. 利用定时器 1和定时器0控制led1和led2分别 2hz和0.5hz闪烁

    //利用定时器 1和定时器0控制led1和led2分别 2hz和0.5hz闪烁 #include<reg52.h> #define uchar unsigned char #define ...

  9. Lucas小记

    组合数学全忘了 记笔记记笔记 做个简单题 代码 from bzoj4403 #include <stdio.h> #define p 1000003 typedef long long l ...

  10. 关于linux中使用vim打开文件出现^M的解决方法

    在linux下,不可避免的会用VIM打开一些windows下编辑过的文本文件.我们会发现文件的每行结尾都会有一个^M符号,这是因为 DOS下的编辑器和Linux编辑器对文件行末的回车符处理不一致, 各 ...