Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

系统化 js模块化知识 #6

Open
heycqing opened this issue Jan 7, 2020 · 0 comments
Open

系统化 js模块化知识 #6

heycqing opened this issue Jan 7, 2020 · 0 comments

Comments

@heycqing
Copy link
Owner

heycqing commented Jan 7, 2020

为什么想去了解 js 模块化

  1. 想去了解,好奇心
  2. 所接触编程的种种经历,内心想让我去了解 js 模块;(求知欲)
  3. js 在现在不单单只是一种脚本语言,随着 js 语言的发展,前端的发展,前端项目越来越复杂,只限于脚本的 js 不满足当前的需要,模块化正使 js 在处理复杂庞大的应用程序上变得可靠和变得可能。在这个基础上了解 js 的模块化十分有必要

自己对模块化的理解

模块是一个可划分,对内封装起来,对外界可提供相关接口的代码块

模块化是模块之间有依赖关系的

读 github 上的文章

  1. 为什么需要模块化?
  2. 模块化是什么样的?
  3. 如何使用模块化?

文章

js 模块化要解决的问题

  1. 命名冲突(或者说同名函数的冲突)
    1. 同名函数的在同一个作用域下的冲突,前面的会被后面的函数覆盖
  2. 大型 js 代码库的 可维护性
    1. 对于大量文件,脚本的手动控制(即通过script标签将脚本放置在页面上)变得非常乏味和法繁琐,因为首先您必须记住将必要的脚本放入页面中,其次要保留脚本的正确顺序。

历史

  1. 直接定义依赖 - 1999

  2. 命名空间模式 - 2002

    1. 命名空间为代码组织提供了某种排序

      // file app.js
      var app = {};
      
      // file greeting.js
      app.helloInLang = {
          en: 'Hello world!',
          es: '¡Hola mundo!',
          ru: 'Привет мир!'
      };
      
      // file hello.js
      app.writeHello = function (lang) {
          document.write(app.helloInLang[lang]);
      };
  3. 模块模式 - 2003

    1. 使用闭包封装了代码和数据,并且提供方法让外界访问到它

      var greeting = (function () {
        var module = {};
      
        var helloInLang = {
            en: 'Hello world!',
            es: '¡Hola mundo!',
            ru: 'Привет мир!'
        };
      
        module.getHello = function (lang) {
            return helloInLang[lang];
        };
      
        module.writeHello = function (lang) {
            document.write(module.getHello(lang))
        };
        
        return module;
      }());

    到了这里,不过都是完成了 隔离 的目标,但是依赖关系呢?

  4. 模块定义依赖关系 - 2006

    1. 使用 js模版 中的特殊字符来代替 <script> 标签来引入其他文件的js文件,合到一个js文件中,可以让开发人员不用理会js文件的顺序
  5. 注释定义依赖关系 - 2006

    1. 利用代码在构建时动态解析库来达到目的,通过找到相关注释的代码,把注释的代码下载下来
  6. 外部定义依赖关系 - 2007

    1. 在代码外部使用对象或者数组来定义依赖关系,常见的是配置文件;

      // file deps.json
      {
          "files": {
              "main.js": ["sayHello.js"],
              "sayHello.js": ["helloInLang.js"],
              "helloInLang.js": []
          }
      }
      
      // file helloInLang.js
      var helloInLang = {
          en: 'Hello world!',
          es: '¡Hola mundo!',
          ru: 'Привет мир!'
      };
      
      // file sayHello.js
      function sayHello(lang) {
          return helloInLang[lang];
      }
      
      // file main.js
      console.log(sayHello('en'));
  7. 沙盒模式 - 2009

    1. 使用一个顶级的对象来代替window的全局作用域,或者说直接是用一个构造函数来代替全局变量。每次都通过 new 一个沙盒对象来构建 文件之间的互相依赖关系。

      // file sandbox.js
      function Sandbox(callback) {
          var modules = [];
      
          for (var i in Sandbox.modules) {
              modules.push(i);
          }
      
          for (var i = 0; i < modules.length; i++) {
              this[modules[i]] = Sandbox.modules[modules[i]]();
          }
          
          callback(this);
      }
      
      // file greeting.js
      Sandbox.modules = Sandbox.modules || {};
      
      Sandbox.modules.greeting = function () {
          var helloInLang = {
              en: 'Hello world!',
              es: '¡Hola mundo!',
              ru: 'Привет мир!'
          };
      
          return {
              sayHello: function (lang) {
                  return helloInLang[lang];
              }
          };
      };
      
      // file app.js
      new Sandbox(function(box) {
          document.write(box.greeting.sayHello('es'));
      });

    到了这里,以上的所有依赖关系都得自己去处理构建的,然后再自己调用

  8. 依赖注入 - 2009

    1. 依赖注入的概念来源于java,目的是组件或者类不用自己创建文件的依赖关系,只需要引入其他文件供自己使用即可
    2. 主流框架 angularJs 核心就是使用了依赖注入的原理

    使用依赖注入的设计模式,可以直接调用其它文件,而不再需要自己创建

  9. CommonJs Modules - 2009

    1. commonJs是在 由于nodeJS 没有适当的模块规范来统一代码分发问题 的情况下诞生的,前身是 serverJS, 由 Mozilla 工程师Kevin Dangoor发起,社区人员一起开发的成果。所以,它是一种规范。

    2. 它是一种模块化的规范,主要是应用在除客户端js以外的Js应用中,包括服务端nodeJs和桌面应用,它是如何工作? --> 在发送给 js 引擎之前,会把一组组的使用commonJs模块规范的js文件打包,

      会被把js代码放到被这样的形式函数中。

    	(function (exports, require, module, __filename, __dirname) {
           // ...
           // Your code injects here!
           // ...
       });
    1. 是我们非常熟悉的两个关键字: module 、require

    2. 使用CommonJs规范的模块都是同步加载的,即第一个模块加载完再会轮到第二个加载

      // file greeting.js
      var helloInLang = {
          en: 'Hello world!',
          es: '¡Hola mundo!',
          ru: 'Привет мир!'
      };
      
      var sayHello = function (lang) {
          return helloInLang[lang];
      }
      
      module.exports.sayHello = sayHello;
      
      // file hello.js
      var sayHello = require('./lib/greeting').sayHello;
      var phrase = sayHello('en');
      console.log(phrase);
      1. 现在大规模使用的就是commonJS模块规范,不仅仅是可以在服务端 nodeJs 上被使用,更可以在 打包工具 webpack 或者 browserfiy 上使用。
  10. ADM (Asynchronous Module Definition) - 2009

    1. 有了同步加载,当然会有异步加载规范,在 CommonJs 全面开启规范工作的同时,有另外一种声音 --- 讨论异步加载模块规范的可能性,讨论的动机即:这(异步加载模块)将有助于加快Web应用程序的加载速度,而无需进行任何预先打包。

    2. 另一名来自Mozilla的工程师James Burke(开发requireJs)提出ADM的基本思想: 模块加载不应该是同步的,更应该是使用浏览器功能来并行加载js脚本文件(也就是使用浏览器自带的功能来实现异步加载)。所以,异步加载模块规范也诞生了。

      	// file lib/greeting.js
      define(function() {
          var helloInLang = {
              en: 'Hello world!',
              es: '¡Hola mundo!',
              ru: 'Привет мир!'
          };
      
          return {
              sayHello: function (lang) {
                  return helloInLang[lang];
              }
          };
      });
      
      // file hello.js
      define(['./lib/greeting'], function(greeting) {
          var phrase = greeting.sayHello('en');
          document.write(phrase);
      });

    这感觉是,两种不同的思想在对抗,但是由于 nodeJS 的蓬勃发展,npm社区的活跃,支持 CommonJS 的开发人员远大于支持 ADM 的开发人员

  11. UMD (Universal Module Definition)- 2011

    UMD (通用模块定义),它的出现很好的兼顾了主张同步模块加载的 commomJs 和 主张异步模块加载的 ADM ,可以在服务器环境和浏览器环境都分别支持 commonJS 和 ADM 。

    主要思想是,通过判断当前的环境是支持commonJS 还是支持 ADM 来使用不同的参数达到目的。

    (function(define) {
        define(function () {
            var helloInLang = {
                en: 'Hello world!',
                es: '¡Hola mundo!',
                ru: 'Привет мир!'
            };
    
            return {
                sayHello: function (lang) {
                    return helloInLang[lang];
                }
            };
        });
    }(
        typeof module === 'object' && module.exports && typeof define !== 'function' ?
        function (factory) { module.exports = factory(); } :
        define
    ));

    这段代码使用立即执行函数来判断当前的规范环境,如果是支持commomJs 环境的,则使用函数 function (factory) { module.exports = factory(); } ,否则则使用 define

    例如在CommonJS环境使用

    为什么会这样做?
    因为当时的支持CommomJs的环境不支持ADM,支持ADMd的环境也不支持CommonJs,两者冲突较大,让整个js生态环境没有得到一个统一的模块规范,呈现一团糟的状态。然后 UMD 的出现,兼顾了2者,得到了众多开发者的拥簇。

  12. Labeled Modules - 2012

标签模块,主要思想是提出可传递模块,(看着代码,是不是感觉有点眼熟,形式类似于c语言的 go to 关键字)。

使用标签来代替模块的引入关系,

这个时候 ES6 已经研发2年了。

```js
// file greeting.js
var helloInLang = {
    en: 'Hello world!',
    es: '¡Hola mundo!',
    ru: 'Привет мир!'
};

exports: var greeting = {
    sayHello: function (lang) {
        return helloInLang[lang];
    }
};

// file hello.js
require: './lib/greeting';
var phrase = greeting.sayHello('es');
document.write(phrase);
```
  1. YModules - 2013

    基于 Yandex 上研发的一个模块化系统,主要是解决CommonJS和ADM没有解决的问题,但是入手的门槛高,有要求。

    反正我没看懂...

  2. ES6 模块规范 - 2015

    ES6规范准备了5年,

    看了代码你可能有点疑惑,这不是ES6规范的代码?

    是的,ES6的一个版本即 ES 6.1 版本在2015年6月发布,正式名称是:《ECMAScript 2015 标准》(简称 ES2015),随后的2016年6月发布小幅修订的《ECMAScript 2016 标准》(简称 ES2016),因为差异过小,都统称 ES6 .

    ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。

    ​ -- 引用阮一峰老师的话

    es6 的模块规范,完全取代了CommonJs 和 ADM 两者,即可以在服务端支持 js 模块化,又可以在客户端支持 js 模块化。不再需要使用 UMD 的兼顾两者了。

    具体链接

    // file lib/greeting.js
    const helloInLang = {
        en: 'Hello world!',
        es: '¡Hola mundo!',
        ru: 'Привет мир!'
    };
    
    export const greeting = {
        sayHello: function (lang) {
            return helloInLang[lang];
        }
    };
    
    // file hello.js
    import { greeting } from "./lib/greeting";
    const phrase = greeting.sayHello("en");
    document.write(phrase);

    done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant