ホームページ  >  記事  >  ウェブフロントエンド  >  requireJS はモジュールローダーをどのように実装しますか?

requireJS はモジュールローダーをどのように実装しますか?

亚连
亚连オリジナル
2018-06-11 17:54:391455ブラウズ

この記事では主に、単純なモジュール ローダーの requireJS 実装について詳しく説明します。それを皆さんと共有し、参考にしてください。

前の記事では、モジュール型プログラミングの重要性と、それによって解決できる問題を何度も強調しました:

① 単一ファイル変数の名前の競合の問題を解決する

② フロントエンドの複数のファイルの問題を解決する人とのコラボレーション

③ ファイルの依存関係の問題を解決する

④ オンデマンドでロードする (この記述は実際には非常に誤りです)

⑤...

ローダーをより深く理解するために、私は、 requireJS のソースコードですが、多くの学生にとって、ローダーの実装はまだあまり明確ではありません

実際、ライブラリやフレームワークを読むだけで理解したい場合は、部分的な理解しかできないので、今日は簡単なローダーを実装します

ローダーの原理の分析

分割と結合

実際、プログラムを実行するには完全なモジュールが必要です。例:

//求得绩效系数
 var performanceCoefficient = function () {
  return 0.2;
 };
 //住房公积金计算方式
 var companyReserve = function (salary) {
  return salary * 0.2;
 };
 //个人所得税
 var incomeTax = function (salary) {
  return salary * 0.2;
 };
 //基本工资
 var salary = 1000;
 //最终工资
 var mySalary = salary + salary * performanceCoefficient();
 mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary));
 console.log(mySalary);

完全な給与の場合、会社は成果報酬を持っていますが、そのアルゴリズムは非常に複雑である可能性があり、その中には出席、修了などが含まれる可能性がありますが、当面は気にしません増加する場合は減少するため、住宅積立金を支払い、最終的には私の給与になります。上記のプロセスは必要不可欠ですが、各機能は異なります。非常に複雑で、お金に関係することも複雑なので、会社の業績だけでコードが1,000行を超える可能性があります

そこで、ここで分割を開始します:

<script src="companyReserve.js" type="text/javascript"></script>
<script src="incomeTax.js" type="text/javascript"></script>
<script src="performanceCoefficient.js" type="text/javascript"></script>
<script type="text/javascript">
 //基本工资
 var salary = 1000;
 //最终工资
 var mySalary = salary + salary * performanceCoefficient();
 mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary));
 console.log(mySalary);
</script>

上記のコードは、「分割」されていることを示していますしかし、実際には、「マージ」の問題も発生します。結局、その中のファイルはまだ残っている可能性があります。依存関係に関して言えば、require と define が登場します。

実際、上記の解決策は依然としてモジュールではなくファイルごとに分割されています。ファイル名が変更されると、ページも変更されます。実際には、この問題に対処するためにここにパスマッピングが必要です。

フロントエンドモジュールにはリクエストが含まれるため、モジュールの 1 つはパス js ファイルに対応し、残りは対応するモジュールをロードすることになります。したがって、この書き方:

var pathCfg = {
 &#39;companyReserve&#39;: &#39;companyReserve&#39;,
 &#39;incomeTax&#39;: &#39;incomeTax&#39;,
 &#39;performanceCoefficient&#39;: &#39;performanceCoefficient&#39;
};

は、どこかで行われているのを見たとしても、そこにはいくつかの「トリック」があるはずです:

companyReserve = requile(&#39;companyReserve&#39;);
は次のとおりです。標準的な requireJS の書き方。最初にモジュールとそのパス マッピングを定義し、依存関係を定義します

require.config({
 &#39;companyReserve&#39;: &#39;companyReserve&#39;,
 &#39;incomeTax&#39;: &#39;incomeTax&#39;,
 &#39;performanceCoefficient&#39;: &#39;performanceCoefficient&#39;
});
require([&#39;companyReserve&#39;, &#39;incomeTax&#39;, &#39;performanceCoefficient&#39;], function (companyReserve, incomeTax, performanceCoefficient) {
 //基本工资
 var salary = 1000;
 //最终工资
 var mySalary = salary + salary * performanceCoefficient();
 mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary));
 console.log(mySalary);
});

シンプルで完全なモジュール ローダーは基本的に次のようになります。最初は依存関係の配列、2 番目はコールバックです。コールバックには必要なものがあります。すべての依存関係は実行前にロードされる必要があり、コールバックのパラメーターは依存関係の実行結果であるため、定義モジュールには通常、戻り値が必要です

ソリューションが利用可能になったので、実装方法を説明します。それ?

実装計画

モジュールの読み込みに関して、人々の最初の反応は ajax です。モジュール ファイルの内容を取得できる場合、それがモジュール化の基礎になりますが、ajax メソッドだけでは十分ではないからです。クロスドメインの問題

があり、モジュール型ソリューションでは必然的にクロスドメインの問題に対処する必要があるため、動的に作成されたスクリプトタグを使用して js ファイルをロードすることが第一の選択肢となっていますが、ajax を使用しないソリューションの実装は困難です。まだ要件があると言われています

追記: 実際の作業では、HTML テンプレート ファイルを読み込むシーンもあります。これについては後ほど説明します通常、これをスケジュール JavaScript のプログラムの入り口として使用します。その後、各モジュールはロードするスクリプト タグをサイレントに作成し、require 内のすべての依存モジュールのロードが完了したことを require モジュール キューに報告します。原理はほぼ同じで、残りは具体的な実装です。そして、この理論が信頼できるかどうかを証明します

ローダー去勢実装

コアモジュール

上記の理論に従って、まず全体的に話しましょう。とりわけ、入り口の 3 つの基本機能について説明しましょう

require(depArr, callback)

これら 3 つのモジュールは不可欠です:

① config はモジュールとパス間のマッピングを設定するために使用され、または他の用途があります

② require はプログラムの入り口です

③ 各モジュールを設計し、require のスケジューリングに応答するために定義します

次に、ここで script タグを作成するメソッドを用意し、その onLoad イベントをリッスンします

④ loadScript

次に、script タグをロードした後、ロードされたモジュールを保存するためのグローバル モジュール オブジェクトである必要があるため、ここで提案します 2 つの要件が満たされています:

⑤ require.moduleObj モジュール ストレージ オブジェクト

⑥ モジュール、モジュール コンストラクター

上記のコア モジュールを使用して、以下を形成しましたコード:

var require = function () {
};
require.config = function () {
};
require.define = function () {
};

そのため、次のステップは具体的な実装です。実装プロセスでは、利用できないインターフェイスや詳細が補完されることが多く、最終的な実装は元の設計とは何の関係もないことがよくあります...

コードの実装

这块最初实现时,本来想直接参考requireJS的实现,但是我们老大笑眯眯的拿出了一个他写的加载器,我一看不得不承认有点妖

于是这里便借鉴了其实现,做了简单改造:

(function () {
 //存储已经加载好的模块
 var moduleCache = {};
 var require = function (deps, callback) {
  var params = [];
  var depCount = 0;
  var i, len, isEmpty = false, modName;
  //获取当前正在执行的js代码段,这个在onLoad事件之前执行
  modName = document.currentScript && document.currentScript.id || &#39;REQUIRE_MAIN&#39;;
  //简单实现,这里未做参数检查,只考虑数组的情况
  if (deps.length) {
   for (i = 0, len = deps.length; i < len; i++) {
    (function (i) {
     //依赖加一
     depCount++;
     //这块回调很关键
     loadMod(deps[i], function (param) {
      params[i] = param;
      depCount--;
      if (depCount == 0) {
       saveModule(modName, params, callback);
      }
     });
    })(i);
   }
  } else {
   isEmpty = true;
  }
  if (isEmpty) {
   setTimeout(function () {
    saveModule(modName, null, callback);
   }, 0);
  }
 };
 //考虑最简单逻辑即可
 var _getPathUrl = function (modName) {
  var url = modName;
  //不严谨
  if (url.indexOf(&#39;.js&#39;) == -1) url = url + &#39;.js&#39;;
  return url;
 };
 //模块加载
 var loadMod = function (modName, callback) {
  var url = _getPathUrl(modName), fs, mod;
  //如果该模块已经被加载
  if (moduleCache[modName]) {
   mod = moduleCache[modName];
   if (mod.status == &#39;loaded&#39;) {
    setTimeout(callback(this.params), 0);
   } else {
    //如果未到加载状态直接往onLoad插入值,在依赖项加载好后会解除依赖
    mod.onload.push(callback);
   }
  } else {
   /*
   这里重点说一下Module对象
   status代表模块状态
   onLoad事实上对应requireJS的事件回调,该模块被引用多少次变化执行多少次回调,通知被依赖项解除依赖
   */
   mod = moduleCache[modName] = {
    modName: modName,
    status: &#39;loading&#39;,
    export: null,
    onload: [callback]
   };
   _script = document.createElement(&#39;script&#39;);
   _script.id = modName;
   _script.type = &#39;text/javascript&#39;;
   _script.charset = &#39;utf-8&#39;;
   _script.async = true;
   _script.src = url;
   //这段代码在这个场景中意义不大,注释了
   //   _script.onload = function (e) {};
   fs = document.getElementsByTagName(&#39;script&#39;)[0];
   fs.parentNode.insertBefore(_script, fs);
  }
 };
 var saveModule = function (modName, params, callback) {
  var mod, fn;
  if (moduleCache.hasOwnProperty(modName)) {
   mod = moduleCache[modName];
   mod.status = &#39;loaded&#39;;
   //输出项
   mod.export = callback ? callback(params) : null;
   //解除父类依赖,这里事实上使用事件监听较好
   while (fn = mod.onload.shift()) {
    fn(mod.export);
   }
  } else {
   callback && callback.apply(window, params);
  }
 };
 window.require = require;
 window.define = require;
})();

首先这段代码有一些问题:

没有处理参数问题,字符串之类皆未处理

未处理循环依赖问题

未处理CMD写法

未处理html模板加载相关

未处理参数配置,baseUrl什么都没有搞

基于此想实现打包文件也不可能

......

但就是这100行代码,便是加载器的核心,代码很短,对各位理解加载器很有帮助,里面有两点需要注意:

① requireJS是使用事件监听处理本身依赖,这里直接将之放到了onLoad数组中了

② 这里有一个很有意思的东西

document.currentScript

这个可以获取当前执行的代码段

requireJS是在onLoad中处理各个模块的,这里就用了一个不一样的实现,每个js文件加载后,都会执行require(define)方法

执行后便取到当前正在执行的文件,并且取到文件名加载之,正因为如此,连script的onLoad事件都省了......

demo实现

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <title></title>
</head>
<body>
</body>
<script src="require.js" type="text/javascript"></script>
<script type="text/javascript">
 require([&#39;util&#39;, &#39;math&#39;, &#39;num&#39;], function (util, math, num) {
  num = math.getRadom() + &#39;_&#39; + num;
  num = util.formatNum(num);
  console.log(num);
 });
</script>
</html>
//util
define([], function () {
 return {
  formatNum: function (n) {
   if (n < 10) return &#39;0&#39; + n;
   return n;
  }
 };
});
//math
define([&#39;num&#39;], function (num) {
 return {
  getRadom: function () {
   return parseInt(Math.random() * num);
  }
 };
});
//math
define([&#39;num&#39;], function (num) {
 return {
  getRadom: function () {
   return parseInt(Math.random() * num);
  }
 };
});

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

有关在Vue2.x中父组件与子组件双向绑定(详细教程)

详细介绍在Vue2.0中v-for迭代语法的变化(详细教程)

在vue2.0中循环遍历并且加载不同图片(详细教程)

以上がrequireJS はモジュールローダーをどのように実装しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。