ホームページ  >  記事  >  ウェブフロントエンド  >  requireJS は単純なモジュールローダーインスタンス共有を実装します

requireJS は単純なモジュールローダーインスタンス共有を実装します

小云云
小云云オリジナル
2018-01-16 13:15:231635ブラウズ

この記事は主に、単純なモジュールローダーを実装する 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 と定義を入力します

require と定義

実際、上記の解決策はまだファイルごとに分割されています。ファイル名が変更された場合、実際には、この問題を処理するためのパス マッピングが必要です

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

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

companyReserve = requile(&#39;companyReserve&#39;);

はフロントエンドには適用できません。これがどこかで行われているのを見たとしても、ここでは AMD の仕様に従う必要があります。

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);
});

これは標準的な requireJS の記述方法です。まず、依存関係を定義するモジュールとそのパス マッピングを定義します


require(depArr, callback)

まず、単純で完全なモジュール ローダーは基本的に次のようになります。コールバックは、実行する前にすべての依存関係をロードする必要があり、コールバックのパラメーターは依存関係の実行の結果であるため、通常、定義モジュールには戻り値が必要です

計画は利用可能ですが、どのように実行すればよいですか?


実装計画

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

があり、モジュール型ソリューションでは必然的にクロスドメインの問題に対処する必要があるため、動的に作成されたスクリプトタグを使用して js ファイルをロードすることが第一の選択肢となっていますが、ajax を使用しないソリューションの実装は困難です。まだ要件があると言われています追記: 実際の作業では、HTML テンプレート ファイルを読み込むシーンもあります。これについては後ほど説明します

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

ローダー去勢実装

コアモジュール

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

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

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

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

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

③ 各モジュールのデザインを定義し、スケジューリングの要求に応答します


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

④ loadScript

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

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

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

上記のコア モジュールを使用して、次のコードを作成しました。


(function () {

 var Module = function () {
  this.status = &#39;loading&#39;; //只具有loading与loaded两个状态
  this.depCount = 0; //模块依赖项
  this.value = null; //define函数回调执行的返回
 };


 var loadScript = function (url, callback) {

 };

 var config = function () {

 };

 var require = function (deps, callback) {

 };

 require.config = function (cfg) {

 };

 var define = function (deps, callback) {

 };

})();

于是接下来便是具体实现,然后在实现过程中补足不具备的接口与细节,往往在最后的实现与最初的设计没有半毛钱关系......

代码实现

这块最初实现时,本来想直接参考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);
  }
 };
});

小结

今天我们实现了一个简单的模块加载器,通过他希望可以帮助各位了解requireJS或者seaJS,最后顺利进入模块化编程的行列。

相关推荐:

用js实现简易模块加载器的方法

概述如何实现一个简单的浏览器端js模块加载器

JavaScript 模块化编程之加载器原理详解

以上がrequireJS は単純なモジュールローダーインスタンス共有を実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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