ホームページ >ウェブフロントエンド >jsチュートリアル >モジュール性は JS でどのように実装されますか?
Js の初期の位置付け (当初は過度に複雑なシナリオで使用されるとは想定していませんでした) により、モジュール システムは提供されません。アプリケーションがより複雑になるにつれて、モジュール化は解決しなければならない問題になります。 Feimai の徹底した原則に従って、モジュール化のベールを取り除く必要があります。この記事では、主に Js でのモジュール化がどのように詳細に実装されているかを紹介し、一定の参考値を持つモジュール化の操作を詳細に紹介します。興味のある方はさらに詳しく学んで、皆さんのお役に立てれば幸いです。
1. モジュール化によって解決すべき問題
何かを深く分析するには、目的を持って見る必要があります。モジュール化によって解決される問題は、一文に要約できます
地球規模の汚染を発生させずに、プロジェクト コードをより適切に構成できます
簡単な例を挙げると、次のコードができました:
function doSomething () { const a = 10; const b = 11; const add = function (a + b) { return a + b } add (a + b) }
実際のアプリケーション内 シナリオ内、 doSomething は非常に多くのことを実行する必要がある可能性があり、追加関数はより複雑で再利用できる可能性があるため、追加関数を別のファイルに分離したいと考えています:
// doSomething.js 文件 const add = require('add.js'); const a = 10; const b = 11; add(a+ b);
// add.js 文件 function add (a, b) { return a + b; } module.exports = add;
これの目的は明らかです。プロジェクト コードをよりよく整理し、2 つのファイル内の require と module.exports に注目してください。現在の観点から見ると、これは CommonJS 仕様のキーワードから来ています (この仕様については後で説明する章があります)。これらはインポートとエクスポートを表します。仕様に関係なく、これは実際にはモジュール化に向けて解決する必要がある問題です。さらに、add モジュールは再利用する必要がありますが、add を導入するときに地球規模の汚染を引き起こすことは望ましくありません
2. インポートされたモジュールを実行する方法
上記の例では、コードを 2 つのモジュールに分割しています。ファイルを地球規模の汚染を引き起こすことなく、例のコードが正常に実行できるように require を実装するにはどうすればよいでしょうか?
モジュールファイルのコードの読み込みプロセスを無視し、requireがすでにモジュールファイルからコード文字列を読み取ることができると仮定すると、requireは次のように実装できます
function require (path) { // lode 方法读取 path 对应的文件模块的代码字符串 // let code = load(path); // 不考虑 load 的过程,直接获得模块 add 代码字符串 let code = 'function add(a, b) {return a+b}; module.exports = add'; // 封装成闭包 code = `(function(module) {$[code]})(context)` // 相当于 exports,用于导出对象 let context = {}; // 运行代码,使得结果影响到 context const run = new Function('context', code); run(context, code); //返回导出的结果 return context.exports; }
いくつかのポイントがあります:
1)原因 グローバル汚染の場合、コード文字列をクロージャの形式でカプセル化する必要があり、キーワード module.exports をエクスポートする必要があります。モジュールは、外部との接触の唯一のキャリアです。これを入力として使用する必要があります。クロージャ匿名関数のパラメータとリファラーによって渡されるコンテキストの関連付け
2) new Function を使用してコード文字列を実行する 一般的にはその必要がないため、ほとんどの学生は new Function に慣れていないものと推定されます。これは関数を定義するためのものです。次のように関数クラスを使用して関数を作成できることを知っておく必要があります。
var function_name = new function(arg1, arg2, ..., argN, function_body)
上記の形式では、各引数はパラメーターであり、最後のパラメーターは関数本体 (関数のコード) です。実行されます)。これらのパラメータは文字列である必要があります。つまり、eval と同様に文字列コードを実行するために使用でき、eval と比較して、文字列コード内の特定の変数の値をパラメーターの形式で渡すこともできます
3)標準のエクスポート キーワードはエクスポートのみであるのに、実際の使用では module.exports を使用する必要があるのはなぜだろうかという疑問を抱いたことがあるでしょう (Node コードを書いたことがある人はよく知っているはずです)。その場合は、このコードで答えを見つけることができます。 use exports コンテキストを受信すると、エクスポートの再割り当てはコンテキストに影響を与えません (パラメーターのアドレスが渡されます)。信じられない場合は、コードを次の形式に変更して再実行してください。
デモの結果
3. コードのロード方法
コードの実行の問題を解決するには、モジュール ファイルのコードのロードの問題も解決する必要があります。上記の例によれば、私たちの目標はモジュールをロードすることです。文字列形式のファイル コード
Node コンテナでは、すべてのモジュール ファイルはローカルにあります。ローカル ディスクからモジュール ファイルを読み取って文字列コードをロードし、上記のプロセスに従うだけです。事実は、Node の非組み込みモジュール、コアモジュール、および C++ モジュールのロードと実行メソッドがほぼ同じであることを証明しています (新しい関数ではありませんが、同様のメソッドです)
RN/Weex コンテナでは、リモートのbundle.jsをロードする必要がある場合、ネイティブの機能を通じてリモートjsファイルをリクエストし、それを文字列コードに読み込んでロードできます(このロジックによれば、Nodeはリモートjsモジュールを読み取ることができるように見えますが)ほとんどの場合、これを行う必要はありません)
ブラウザ環境では、すべての JS モジュールをリモートで読み取る必要がありますが、残念ながら、ブラウザが提供する機能の制限により、リモート JS ファイルを次の形式で直接読み取ることができません。 ajax を介したファイル ストリームは文字列コードです。前提条件が満たされない場合、上記の運用戦略は機能せず、別の方法を見つけるしかありません
これが CommonJs 仕様が存在する理由であり、AMD/CMD 仕様も存在する理由です
それではどうでしょうかブラウザ上で完了しますか?ブラウザの Js コントロールを通じてリモート Js モジュール ファイルを動的にロードするには、<script> ノードを動的に挿入する必要があります。<pre class="brush:php;toolbar:false">// 摘抄自 require.js 的一段代码
var node = config.xhtml ?
document.createElementNS(&#39;http://www.w3.org/1999/xhtml&#39;, &#39;html:script&#39;) :
document.createElement(&#39;script&#39;);
node.type = config.scriptType || &#39;text/javascript&#39;;
node.charset = &#39;utf-8&#39;;
node.async = true;
node.setAttribute(&#39;data-requirecontext&#39;, context.contextName);
node.setAttribute(&#39;data-requiremodule&#39;, moduleName);
node.addEventListener(&#39;load&#39;, context.onScriptLoad, false);
node.addEventListener(&#39;error&#39;, context.onScriptError, false);<p>要知道,设置了 <script> 标签的 src 之后,代码一旦下载完成,就会立即执行,根本由不得你再封装成闭包,所以文件模块需要在定义之初就要做文章,这就是我们说熟知的 AMD/CMD 规范中的 define,开篇的 add.js 需要重新改写一下</script></p>
<pre class="brush:php;toolbar:false">// add.js 文件
define ('add',function () {
function add (a, b) {
return a + b;
}
return add;
})</pre>
<p>而对于 define 的实现,最重要的就是将 callback 的执行结果注册到 context 的一个模块数组中:</p>
<pre class="brush:php;toolbar:false"> context.modules = {}
function define(name, callback) {
context.modules[name] = callback && callback()
}</pre>
<p>于是 require 就可以从 context.modules 中根据模块名载入模块了,是不是有了一种自己去写一个 “requirejs” 的冲动感</p>
<p>具体的 AMD 实现当然还会复杂很多,还需要控制模块载入时序、模块依赖等等,但是了解了这其中的灵魂,想必去精读 require.js 的源码也不是一件困难的事情</p>
<p>四、Webpack 中的模块化</p>
<p>Webpack 也可以配置异步模块,当配置为异步模块的时候,在浏览器环境同样的是基于动态插入 <script> 的方式载入远程模块。在大多数情况下,模块的载入方式都是类似于 Node 的本地磁盘同步载入的方式</script>
嫑忘记,Webpack 除了有模块化的能力,还是一个在辅助完善开发工作流的工具,也就是说,Webpack 的模块化是在开发阶段的完成的,使用 Webpack 构筑的工作环境,在开发阶段虽然是独立的模块文件,但是在运行时,却是一个合并好的文件
所以 Webpack 是一种在非运行时的模块化方案(基于 CommonJs),只有在配置了异步模块的时候对异步模块的加载才是运行时的(基于 AMD)
五、模块化规范
通用的问题在解决的过程中总会形成规范,上文已经多次提到 CommonJs、AMD、CMD,有必要花点篇幅来讲一讲规范
Js 的模块化规范的萌发于将 Js 扩展到后端的想法,要使得 Js 具备类似于 Python、Ruby 和 Java 那样具备开发大型应用的基础能力,模块化规范是必不可少的。CommonJS 规范的提出,为Js 制定了一个美好愿景,希望 Js 能在任何地方运行,包括但不限于:
服务器端 Js 应用
命令行工具
桌面应用
混合应用
CommonJS 对模块的定义并不复杂,主要分为模块引用、模块定义和模块标识
模块引用:使用 require 方法来引入一个模块
模块定义:使用 exports 导出模块对象
模块标识:给 require 方法传入的参数,小驼峰命名的字符串、相对路径或者绝对路径
模块示意
CommonJs 规范在 Node 中大放异彩并且相互促进,但是在浏览器端,鉴于网络的原因,同步的方式加载模块显然不太实用,在经过一段争执之后,AMD 规范最终在前端场景中胜出(全称 Asynchronous Module Definition,即“异步模块定义”)
什么是 AMD,为什么需要 AMD ?在前述模块化实现的推演过程中,你应该能够找到答案
除此之外还有国内玉伯提出的 CMD 规范,AMD 和 CMD 的差异主要是,前者需要在定义之初声明所有的依赖,后者可以在任意时机动态引入模块。CMD 更接近于 CommonJS
两种规范都需要从远程网络中载入模块,不同之处在于,前者是预加载,后者是延迟加载
相关推荐:
javascript高级模块化require.js的具体使用方法实例分享
JavaScript使用高级模块化require.js的具体方法详解
以上がモジュール性は JS でどのように実装されますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。