この記事は主に、単純なモジュールローダーを実装する 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);
<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 = { 'companyReserve': 'companyReserve', 'incomeTax': 'incomeTax', 'performanceCoefficient': 'performanceCoefficient' };
したがって、モジュールの 1 つはパス js ファイルに対応します。フロントエンド モジュールにはリクエストが含まれるため、残りは対応するモジュールをロードします。したがって、この書き方
companyReserve = requile('companyReserve');
はフロントエンドには適用できません。これがどこかで行われているのを見たとしても、ここでは AMD の仕様に従う必要があります。
require.config({ 'companyReserve': 'companyReserve', 'incomeTax': 'incomeTax', 'performanceCoefficient': 'performanceCoefficient' }); require(['companyReserve', 'incomeTax', 'performanceCoefficient'], function (companyReserve, incomeTax, performanceCoefficient) { //基本工资 var salary = 1000; //最终工资 var mySalary = salary + salary * performanceCoefficient(); mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary)); console.log(mySalary); });
require(depArr, callback)
モジュールの読み込みに関して、人々の最初の反応は ajax です。モジュール ファイルの内容を取得できる場合、それがモジュール化の基礎になりますが、ajax メソッドだけでは十分ではないからです。クロスドメインの問題
があり、モジュール型ソリューションでは必然的にクロスドメインの問題に対処する必要があるため、動的に作成されたスクリプトタグを使用して js ファイルをロードすることが第一の選択肢となっていますが、ajax を使用しないソリューションの実装は困難です。まだ要件があると言われています追記: 実際の作業では、HTML テンプレート ファイルを読み込むシーンもあります。これについては後ほど説明します
通常、これをスケジュール JavaScript のプログラムの入り口として使用します。その後、各モジュールはロードするスクリプト タグをサイレントに作成し、require 内のすべての依存モジュールのロードが完了したことを require モジュール キューに報告します。原理はほぼ同じで、残りは具体的な実装です。そして、この理論が信頼できるかどうかを証明します
ローダー去勢実装
コアモジュール
上記の理論に従って、まず全体的に話しましょう。とりわけ、入り口の 3 つの基本機能について説明しましょう
var require = function () { }; require.config = function () { }; require.define = function () { };
次に、スクリプト タグを作成し、その onLoad イベントをリッスンするメソッドをここに用意します
④ loadScript
次に、スクリプト タグをロードした後、ロードされたモジュールを保存するためのグローバル モジュール オブジェクトである必要があるため、ここでは 2 つの要件を提示します:
⑤ require.moduleObj モジュール ストレージ オブジェクト
⑥ モジュール、モジュール コンストラクター
上記のコア モジュールを使用して、次のコードを作成しました。
(function () { var Module = function () { this.status = 'loading'; //只具有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 || 'REQUIRE_MAIN'; //简单实现,这里未做参数检查,只考虑数组的情况 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('.js') == -1) url = url + '.js'; return url; }; //模块加载 var loadMod = function (modName, callback) { var url = _getPathUrl(modName), fs, mod; //如果该模块已经被加载 if (moduleCache[modName]) { mod = moduleCache[modName]; if (mod.status == 'loaded') { setTimeout(callback(this.params), 0); } else { //如果未到加载状态直接往onLoad插入值,在依赖项加载好后会解除依赖 mod.onload.push(callback); } } else { /* 这里重点说一下Module对象 status代表模块状态 onLoad事实上对应requireJS的事件回调,该模块被引用多少次变化执行多少次回调,通知被依赖项解除依赖 */ mod = moduleCache[modName] = { modName: modName, status: 'loading', export: null, onload: [callback] }; _script = document.createElement('script'); _script.id = modName; _script.type = 'text/javascript'; _script.charset = 'utf-8'; _script.async = true; _script.src = url; //这段代码在这个场景中意义不大,注释了 // _script.onload = function (e) {}; fs = document.getElementsByTagName('script')[0]; fs.parentNode.insertBefore(_script, fs); } }; var saveModule = function (modName, params, callback) { var mod, fn; if (moduleCache.hasOwnProperty(modName)) { mod = moduleCache[modName]; mod.status = 'loaded'; //输出项 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(['util', 'math', 'num'], function (util, math, num) { num = math.getRadom() + '_' + num; num = util.formatNum(num); console.log(num); }); </script> </html>
//util define([], function () { return { formatNum: function (n) { if (n < 10) return '0' + n; return n; } }; });
//math define(['num'], function (num) { return { getRadom: function () { return parseInt(Math.random() * num); } }; });
//math define(['num'], function (num) { return { getRadom: function () { return parseInt(Math.random() * num); } }; });
今天我们实现了一个简单的模块加载器,通过他希望可以帮助各位了解requireJS或者seaJS,最后顺利进入模块化编程的行列。
相关推荐:
以上がrequireJS は単純なモジュールローダーインスタンス共有を実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。