Maison >interface Web >js tutoriel >Une brève introduction à la modularité JavaScript

Une brève introduction à la modularité JavaScript

阿神
阿神original
2017-02-03 17:48:011313parcourir

Avant-propos

Concernant la modularisation, la manifestation la plus directe est les mots-clés require et import que nous avons écrits If If you. Recherchez des informations pertinentes, vous rencontrerez certainement des termes tels que CommonJS et CMD AMD, ainsi que des frameworks inconnus tels que RequireJS et SeaJS. Par exemple, le site officiel de SeaJS se décrit ainsi : "Spécification de définition de module simple et conviviale, Sea.js suit la spécification CMD. Organisation naturelle et intuitive du code, chargement automatique des dépendances..."

As un novice du front-end, je suis honnête. Il avait l'air confus et ne pouvait pas comprendre. Selon mon style habituel, avant d'introduire quelque chose, je dois toujours expliquer pourquoi c'est nécessaire.


Bases de JavaScript

Les étudiants travaillant sur le client sont familier avec OC Vous devez être familier avec #import "classname", les modificateurs de module et de fichier de Swift et le modèle de classe de package d'importation de Java. Nous sommes habitués au modèle selon lequel le référencement à un fichier fait référence à une classe. Cependant, dans un langage dynamique comme JavaScript, les choses ont changé :

<html>
  <head>
    <script type="text/javascript" src="index.js"></script>
  </head>

  <body>
    <p id="hello"> Hello Wrold </p>
    <input type="button" onclick="onPress()" value="Click me" />
  </body>
 </html>
// index.js
function onPress() {
    var p = document.getElementById(&#39;hello&#39;);
    p.innerHTML = &#39;Hello bestswifter&#39;;
}

La balise 3f1c4e4b6b16bbbd69b2ee476dc4f83a Vous pouvez appeler la fonction onPress définie dans index.js.

Supposons qu'au fur et à mesure que le projet se développe, nous constatons que le texte cliqué doit être généré dynamiquement et généré par d'autres fichiers JS, alors un simple index.js ne sera pas en mesure de faire le travail. Nous supposons que le contenu généré est défini dans le fichier math.js :

// math.js
function add(a, b) {
    return a + b;
}

Selon la réflexion du client, le fichier index.js à ce moment devrait être écrit comme ceci :

// index.js
import "math.js"

function onPress() {
    var p = document.getElementById(&#39;hello&#39;);
    p.innerHTML = add(1, 2);
}

Malheureusement, JavaScript ne prend pas en charge la méthode d'importation, ce qui signifie que les méthodes d'autres fichiers JS ne peuvent pas être référencées dans un seul fichier JS. La bonne solution est d'appeler la méthode add directement dans index.js, et en même temps de référencer math.js dans index.html :

<html>
  <head>
    <script type="text/javascript" src="index.js"></script>
    <script type="text/javascript" src="math.js"></script>
  </head>

  <body>
    <p id="hello"> Hello Wrold </p>
    <input type="button" onclick="onPress()" value="Click me" />
  </body>
</html>
// index.js
function onPress() {
    var p = document.getElementById(&#39;hello&#39;);
    p.innerHTML = add(1, 2);
}

Vous pouvez voir que cette méthode d'écriture n'est pas élégant, index.js n'a aucun contrôle sur le contenu des autres fichiers JS. La possibilité d'appeler la méthode add dépend entièrement du fait que votre propre fichier HTML a correctement référencé d'autres fichiers JS.


Modularisation préliminaire

Les points douloureux qui viennent d'être mentionnés dans en fait, il peut être divisé en deux types :

1.index.js ne peut pas être importé et s'appuie sur des références HTML

2.index.js ne peut pas distinguer la source de la méthode add et manque un espace de noms Le concept de

La première question recevra une réponse plus tard. Résolvons d'abord la deuxième question, par exemple, mettons d'abord la fonction dans un objet, afin que nous puissions exposer un objet et laisser l'utilisateur l'appeler. object. Plusieurs méthodes :

//index.js 
function onPress() {
    var p = document.getElementById(&#39;hello&#39;);
    p.innerHTML = math.add(1, 2);
}

//math.js
var math = {
    base: 0,
    add: function(a, b) {
        return a + b + base;
    },
};

Vous pouvez voir qu'une version simplifiée de l'espace de noms (c'est-à-dire mathématique) peut être spécifiée dans index.js. Mais il reste encore un petit problème. Par exemple, l'attribut de base sera exposé au monde extérieur et pourra également être modifié. Une meilleure façon est donc de définir les mathématiques dans une fermeture pour masquer les propriétés internes :

// math.js
var math = (function() {
    var base = 0;
    return {
        add: function(a, b) {
            return a + b + base;
        },
    };
})();

Jusqu'à présent, nous avons implémenté la définition et l'utilisation des modules. Cependant, l'essence de la modularisation réside dans l'espace de noms, ce qui signifie que nous espérons que notre module mathématique ne sera pas global, mais importé à la demande, même si plusieurs fichiers exposent des objets portant le même nom, il n'y en aura pas. problème. Tout comme dans node.js, le module qui doit être exposé définit son propre contenu d'exportation, puis l'appelant utilise la méthode require.

En fait, vous pouvez simplement simuler le mode de fonctionnement de node.js et le résoudre en ajoutant une couche intermédiaire : définissez d'abord une variable globale :

// global.js
var module = {
    exports: {}, // 用来存储所有暴露的内容
};

puis exposez l'objet dans math.js :

var math = (function() {
    var base = 0;
    return {
        add: function(a, b) {
            return a + b + base;
        },
    };
})();

module.exports.math = math;

L'utilisateur index.js devrait maintenant être :

var math = module.exports.math;

function onPress() {
    var p = document.getElementById(&#39;hello&#39;);
    // math
    p.innerHTML = math.add(1, 2);
}


Solution modulaire existante

L'approche modulaire simple ci-dessus présente quelques problèmes mineurs. Tout d'abord, index.js doit strictement s'appuyer sur l'exécution de math.js, car ce n'est qu'après l'exécution de math.js qu'il s'enregistrera auprès du module global.export. Cela oblige les développeurs à gérer manuellement l'ordre de chargement des fichiers js. À mesure que les projets prennent de l’ampleur, la maintenance des dépendances devient de plus en plus complexe.

Deuxièmement, puisque le navigateur cessera de restituer la page Web lors du chargement du fichier JS, nous avons également besoin d'un chargement asynchrone à la demande du fichier JS.

Le dernier problème est que la version simplifiée de la solution de modularisation donnée précédemment ne résout pas l'espace de noms du module. La même exportation remplacera toujours le contenu précédent, et la solution est de conserver un "chemin de fichier< ;--". > Exporter le contenu" et chargez-le en fonction du chemin du fichier.

Sur la base des besoins ci-dessus, de nombreux ensembles de solutions modulaires sont apparus sur le marché. La raison pour laquelle il existe plusieurs ensembles de normes est en fait due aux caractéristiques du frontal. En raison de l'absence d'une norme unifiée, dans de nombreux cas, tout le monde s'appuie sur des conventions pour faire des choses, comme l'exportation et les exigences mentionnées ci-dessus. Si le fournisseur du code stocke le contenu de l'exportation dans module.exports et que l'utilisateur lit module.export, c'est naturellement en vain. De plus, les méthodes de mise en œuvre et les scénarios d'utilisation de chaque spécification sont également différents.


CommonJS

比较知名的规范有 CommonJS、AMD 和 CMD。而知名框架 Node.js、RequireJS 和 Seajs 分别实现了上述规范。

最早的规范是 CommonJS,Node.js 使用了这一规范。这一规范和我们之前的做法比较类似,是同步加载 JS 脚本。这么做在服务端毫无问题,因为文件都存在磁盘上,然而浏览器的特性决定了 JS 脚本需要异步加载,否则就会失去响应,因此 CommonJS 规范无法直接在浏览器中使用。


AMD

浏览器端著名的模块管理工具 Require.js 的做法是异步加载,通过 Webworker 的 importScripts(url); 函数加载 JS 脚本,然后执行当初注册的回调。Require.js 的写法是:

require([&#39;myModule1&#39;, &#39;myModule2&#39;], function (m1, m2){
    // 主回调逻辑
    m1.printName();
    m2.printName();
});

由于这两个模块是异步下载,因此哪个模块先被下载、执行并不确定,但可以肯定的是主回调一定在所有依赖都被加载完成后才执行。

Require.js 的这种写法也被称为前置加载,在写主逻辑之前必须指定所有的依赖,同时这些依赖也会立刻被异步加载。

由 Require.js 引申出来的规范被称为 AMD(Asynchronous Module Definition)。


CMD

另一种优秀的模块管理工具是 Sea.js,它的写法是:

define(function(require, exports, module) {
    var foo = require(&#39;foo&#39;); // 同步
    foo.add(1, 2); 
    ...
    require.async(&#39;math&#39;, function(math) { // 异步
        math.add(1, 2);
    });
});

Sea.js 也被称为就近加载,从它的写法上可以很明显的看到和 Require.js 的不同。我们可以在需要用到依赖的时候才申明。

Sea.js 遇到依赖后只会去下载 JS 文件,并不会执行,而是等到所有被依赖的 JS 脚本都下载完以后,才从头开始执行主逻辑。因此被依赖模块的执行顺序和书写顺序完全一致。

由 Sea.js 引申出来的规范被称为 CMD(Common Module Definition)。


ES 6 模块化

在 ES6 中,我们使用 export 关键字来导出模块,使用 import 关键字引用模块。需要说明的是,ES 6 的这套标准和目前的标准没有直接关系,目前也很少有 JS 引擎能直接支持。因此 Babel 的做法实际上是将不被支持的 import 翻译成目前已被支持的 require。

尽管目前使用 import 和 require 的区别不大(本质上是一回事),但依然强烈推荐使用 import 关键字,因为一旦 JS 引擎能够解析 ES 6 的 import 关键字,整个实现方式就会和目前发生比较大的变化。如果目前就开始使用 import 关键字,将来代码的改动会非常小。


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn