1 はじめに
Web サイトはますます複雑になり、js コードと js ファイルがますます増え、いくつかの問題が発生します。
- ファイルの依存関係
- 地球規模の汚染、命名の衝突
プログラムのモジュール化には次のものが含まれます。
- 日付モジュール
- 数理計算モジュール
- ログモジュール
- ログイン認証モジュール
- レポート表示モジュールなど
これらすべてのモジュールが一緒になってプログラム ソフトウェア システムを構成します。
一度書いて何度も使うことが効率向上の核心です。
2. モジュールの理解
2.1 モジュール性とは
概念: 複雑なプログラムを特定のルール (規格) に従って複数のブロック (ファイル) にカプセル化し、それらを結合します。
モジュールの内部データと実装はプライベートであり、他の外部モジュールと通信するために一部のインターフェイス (メソッド) のみが外部に公開されます。
初期の段階では、すべてのコードを js ファイルに記述していましたが、結合度が高く (相関関係が強い)、メンテナンスに役立たず、地球規模の汚染を引き起こし、競合の名前が付けられやすくなります。
2.2 モジュール化の利点
名前の競合を回避し、名前空間の汚染を減らし
、結合を減らします。分離を改善し、オンデマンドでロードします。
高い再利用性: コードは再利用しやすく、他の人が開発したモジュールを直接使用でき、同様の機能を繰り返し開発する必要がありません。
高い保守性: ソフトウェアステートメントサイクルの中で最も長い段階は、実際には開発段階ではなく、要件が頻繁に変更される保守段階です。モジュール開発を使用すると、保守が容易になります。
導入が簡単
3. モジュール仕様
3.1 モジュール仕様の導入
モジュール化を導入すると仮定すると、最初に思い浮かぶのは、複数の js ファイルを 1 つのファイルに導入するということです。次のように:
<body>
<script src="zepto.js"></script>
<script src="fastClick.js"></script>
<script src="util/login.js"></script>
<script src="util/base.js"></script>
<script src="util/city.js"></script>
</body>
しかし、そうすると多くの問題が発生します。
- リクエストが多すぎます: 10 個の js ファイルが導入されると、10 個の http リクエストが発生します。
- あいまいな依存関係: 異なる js ファイルが相互に依存している可能性があり、ファイルの 1 つが変更されると、別のファイルがエラーを報告する可能性があります。
上記の 2 つの点は、最終的に次のような問題につながります。 保守が困難です。したがって、これによりモジュール仕様が導入されます。
3.2 モジュール化の概念の解釈
モジュール性は Node.js から生まれました。Node.js では、多くの js がパッケージ化され、必要に応じて require (CommonJS) を通じて直接呼び出されるモジュール化の方法です。
では、このモジュラー思考をフロントエンドに適用するにはどうすればよいでしょうか? これにより、RequireJS と SeaJS という 2 つの優れた JS が生まれます。
3.3 モジュール仕様
サーバー側の仕様:
-
CommonJS 仕様: Node.js で使用されるモジュール仕様です。
– CommonJS は一連の規約標準であり、テクノロジーではありません。コードがどうあるべきかについて合意するために使用される構造。 -
ブラウザ側の仕様:
AMD 仕様: プロモーション プロセス中のRequireJS のモジュール定義の標準化された出力です。
– モジュールを非同期でロードする
– 事前依存関係と早期実行: require([foo
,bar
],function(foo,bar){}); //つまり、すべてのパッケージを正常に要求し、コードの実行を続行します。
–define はモジュールを定義します。define([require
,foo
],function(){return}); -
CMD 仕様: プロモーション プロセス中のSeaJS のモジュール定義の標準化された出力です。タオバオチームによって開発されました。
同步加载模块; 依赖就近,延迟执行:require(./a) 直接引入。或者Require.async 异步引入。 //依赖就近:执行到这一部分的时候,再去加载对应的文件。 define 定义模块, export 导出:define(function(require, export, module){});
PS: 面接中に、AMD と CMD の違いについてよく質問されます。
さらに、ES6 の仕様としてインポートとエクスポートがあります。この記事では、Node.js で使用されるモジュール仕様である CommonJS について説明します。
4. CommonJSの基本構文
4.1 CommonJS の概要
CommonJS: Node.js で使用されるモジュール化仕様です。つまり、Node.js は CommonJS のモジュール仕様に基づいて記述されています。
CommonJS 仕様では次のように規定されています。各モジュール内では、モジュール変数が現在のモジュールを表します。この変数はオブジェクトであり、そのエクスポート属性 (つまり module.exports) は外部インターフェイス オブジェクトです。モジュールをロードすると、実際にはモジュールの module.exports オブジェクトがロードされます。
CommonJS では、各ファイルをモジュールとみなすことができます。
サーバー側: モジュールは実行時に同期してロードされます。
ブラウザ側: モジュールは事前にコンパイルしてパッケージ化する必要があります。まず第一に、同期であるためブロッキングが発生しやすいこと、第二に、ブラウザは require 構文を理解できないため、事前にコンパイルおよびパッケージ化する必要があることです。
4.2 モジュールの公開と導入
Node.js にはモジュール レベルのスコープのみがあります。2 つのモジュール間の変数とメソッドは、デフォルトでは互いに競合せず、相互に影響しません。これにより、モジュール A が変数とメソッドをどのように使用するかという問題が発生します。モジュールBで?これは、exports キーワードを通じて実現する必要があります。
Node.js では、各モジュールにエクスポート インターフェイス オブジェクトがあり、パブリック変数とメソッドをこのインターフェイス オブジェクトにマウントし、他のモジュールがそれを使用できます。
次に、モジュールの露出とモジュールの導入について詳しく説明します。
(1) モジュールを公開する最初の方法:exports
exports
オブジェクトは、現在のモジュールのパブリック メソッドまたはプロパティをエクスポートするために使用されます。他のモジュールがrequire
関数を通じて現在のモジュールを呼び出すと、取得するのはexports
現在のモジュールのオブジェクトです。
文法形式:
// 相当于是:给 exports 对象添加属性
exports.xxx = value
値は任意のデータ型にすることができます。
注: 公開されたキーワードはエクスポートではなく、エクスポートです。実際、ここでのエクスポートは、指定された名前のオブジェクトをエクスポートするために使用される ES6 のエクスポートの使用法に似ています。
コード例:
const name = 'qianguyihao';
const foo = function (value) {
return value * 2;
};
exports.name = name;
exports.foo = foo;
(2) モジュールを公開する 2 番目の方法:module.exports
module.exports
オブジェクト名を指定せずにデフォルトのオブジェクトをエクスポートするために使用されます。
文法形式:
// 方式一:导出整个 exports 对象
module.exports = value;
// 方式二:给 exports 对象添加属性
module.exports.xxx = value;
値は任意のデータ型にすることができます。
コード例:
// 方式1
module.exports = {
name: '我是 module1',
foo(){
console.log(this.name);
}
}
// 我们不能再继续写 module.exports = value2。因为重新赋值,会把 exports 对象 之前的赋值覆盖掉。
// 方式2
const age = 28;
module.exports.age = age;
module.exports
モジュールの元のエクスポートされたオブジェクトを変更することも可能です。たとえば、現在のモジュールは元々オブジェクトをエクスポートしていましたが、module.exports を通じて関数をエクスポートするようにそれを変更できます。次のように:
module.exports = function () {
console.log('hello world')
}
4.3 エクスポートと module.exports の違い
最も重要な違い:
- エクスポートを使用する場合、設定できるプロパティは、exports.a = a single; のみです。
- module.exports を使用する場合、プロパティ module.exports.a を個別に設定することも、全体として module.exports = obj を割り当てることもできます。
その他の点:
- Node の各モジュールの最後に実行されます
return: module.exports
。 - Node の各モジュールは、 module.exports が指すオブジェクトを変数 exports、つまり に割り当てます
exports = module.exports
。 module.exports = XXX
、現在のモジュールが単一のメンバーをエクスポートし、結果が XXX であることを示します。- 複数のメンバーをエクスポートする必要がある場合に使用する必要があります
exports.add = XXX; exports.foo = XXX
。または を使用しますmodule.exports.add = XXX; module.export.foo = XXX
。
4.4 質問: 公開されたモジュールは正確には誰ですか?
回答: 露出の本質はexports
オブジェクトです。【★★★★★
】
たとえば、方法 1 は、exports.a = a
エクスポート オブジェクトに属性を追加するものとして理解できます。2 番目の方法は、module.exports = a
エクスポート オブジェクト全体に値を割り当てるものとして理解できます。2 番目の方法は、module.exports.c = c
エクスポート オブジェクトに属性を追加するものとして理解できます。
Node.js の各モジュールにはモジュール オブジェクトがあり、モジュール オブジェクト内のエクスポート属性はインターフェイス オブジェクトと呼ばれます。他のモジュールの利便性を考慮して、このインターフェイス オブジェクト内のモジュール間で共通のメソッドまたはプロパティをマウントする必要があります。
4.5 モジュールをインポートする方法: 必須
require 関数は、モジュールを別のモジュールにインポートするために使用されます。モジュール名を渡し、モジュール エクスポート オブジェクトを返します。
文法形式:
const module1 = require('模块名');
説明:
- 組み込みモジュール: require はパッケージ名です。
- ダウンロードされたサードパーティ モジュール: require はパッケージ名です。
- カスタム モジュール: require はファイル パスです。ファイル パスには絶対パスまたは相対パスを使用できます。接尾辞 .js は省略できます。
コード例:
const module1 = require('./main.js');
const module2 = require('./main');
const module3 = require('Demo/src/main.js');
require() 関数の 2 つの関数:
- インポートされたモジュール内のコードを実行します。
- インポートされたモジュールのインターフェイス オブジェクトを返します。
4.6 メインモジュール
メイン モジュールはプログラム全体の実行のエントリ ポイントであり、他のモジュールをスケジュールできます。
# 运行main.js启动程序。此时,main.js就是主模块
$ node main.js
4.7 モジュールの初期化
モジュール内の JS コードは、モジュールが初めて使用されるときに 1 回だけ実行され、使用中に初期化され、その後の使用のためにキャッシュされます。
コード例:
(1) calModule.js:
var a = 1;
function add () {
return ++a;
}
exports.add = add;
(2) main.js: (main.jsにhello.jsモジュールを導入)
var addModule1 = require('./calModule')
var addModule2 = require('./calModule')
console.log(addModule1.add());
console.log(addModule2.add());
コマンドラインでノード main.js 実行プログラムを実行し、結果を出力します。
2
3
出力結果からわかるように、モジュール calModule.js は 2 回参照されていますが、初期化は 1 回だけです。
5.commonjs - サーバーサイドの実装例
(1)module1.js:
//暴露方式一:module.exports = value
//暴露一个对象出去
module.exports = {
name: '我是 module1',
foo(){
console.log(this.name);
}
}
//我们不能再继续写 module.exports = xxx。因为重新赋值,会把之前的赋值覆盖掉。
(2)module2.js:
//暴露方式一:module.exports = value
//暴露一个函数出去
module.exports = function(){
console.log('我是 module2');
}
このとき公開されるexportsオブジェクトが関数全体に相当することに注意してください。
(3)module3.js:
//暴露方式二:exports.xxx = value
//可以往 export 对象中不断地添加属性,进行暴露
exports.foo1 = function(){
console.log('module3 中的 foo1 方法');
}
exports.foo2 = function(){
console.log('module3 中的 foo2 方法');
}
exports.arr = [1,1,2,2,3,5,11];
(4) app.js:(他のモジュールをメインモジュールにまとめます)
//将其他模块汇集到主模块
let uniq = require('uniq'); //引入时,第三方模块要放在自定义模块的上面
let module1 = require('./modules/module1');
let module2 = require('./modules/module2');
let module3 = require('./modules/module3');
//调用module1对象的方法
module1.foo();
//调用module2的函数
module2(); //注意,在定义时,module2对象等价于整个函数function。所以,module2()的意思是,直接调用了函数。
//调用module3中的属性
module3.foo1();
module3.foo2();
uniq(module3.arr); //将module3中的数组进行去重操作
console.log(module3.arr); //打印数组去重后的结果
この場合、コードは完成です。
コマンドラインに「node app.js」と入力すると、コードを実行できます。印刷結果は次のとおりです。
我是 module1
我是 module2
module3 中的 foo1 方法
module3 中的 foo2 方法
[ 1, 11, 2, 3, 5 ]
6. Commonjsブラウザベースの実装例
6.1 プロジェクトの初期化
プロジェクト ファイルに次のディレクトリとファイルを作成します。
dist //打包生成文件的目录
src //源码所在的目录
| module1.js
| module2.js
| module3.js
| app.js //应用主源文件
index.html //因为CommonJS是基于浏览器端,js文件要跑在浏览器的页面上,所以要有这个html页面
次に、ルート ディレクトリに次のコマンドを作成します。
npm init
次に、プロンプトに従い、以下を順番に入力します。
- パッケージ名: 独自のパッケージ名を選択することも、デフォルトのパッケージ名を使用することもできます。パッケージ名に漢字や大文字を使用することはできないことに注意してください。
- バージョン: デフォルトのバージョン 1.0.0 を使用することも、パッケージ名を自分で変更することもできます。
他のパラメータの場合は、Enter を全押しします。
したがって、package.json ファイルはルート ディレクトリに自動的に生成されます。クリックして見てください:
{
"name": "commonjs_browser",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
6.2 サードパーティのパッケージをダウンロードします。Browserify
ここでは、 Browserifyツールを使用してコンパイルおよびパッケージ化する必要があります。Browserify は、CommonJS のブラウザ側バンドル ツールです。
次のコマンドを入力してインストールします: (両方のコマンドを入力する必要があります)
npm install browserify -g //全局
npm install browserify --save-dev //局部。
上記のコードでは、-dev は開発依存関係を意味します。関連する概念については次のとおりです。
開発の依存関係: 現在、このパッケージは開発環境でのみ使用されます。
依存関係の実行: このパッケージは現在実稼働環境で使用されています。
6.3 カスタムモジュールとコードの実行
(1)module1.js:
//暴露方式一:module.exports = value
//暴露一个对象出去
module.exports = {
name: '我是 module1',
foo(){
console.log(this.name);
}
}
//我们不能再继续写 module.exports = xxx。因为重新赋值,会把之前的赋值覆盖掉。
(2)module2.js:
//暴露方式一:module.exports = value
//暴露一个函数出去
module.exports = function(){
console.log('我是 module2');
}
このとき公開されるexportsオブジェクトが関数全体に相当することに注意してください。
(3)module3.js:
//暴露方式二:exports.xxx = value
//可以往export对象中不断地添加属性,进行暴露
exports.foo1 = function(){
console.log('module3 中的 foo1 方法');
}
exports.foo2 = function(){
console.log('module3 中的 foo2 方法');
}
(4) app.js:(他のモジュールをメインモジュールにまとめます)
導入されたパスの説明:
./
現在のパス (app.js の現在のパスは src) を参照する相対パスです。これでメインのコードは完成です。ただし、以下のようにindex.htmlに直接記述すると動作しません(ブラウザがrequireキーワードを認識しないため)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script src="./js/src/app.js"></script>
</body>
</html>
Index.html が app.js をインポートできるようにするには、次のコマンドを入力する必要があります。
- パッケージ処理js:
browserify js/src/app.js -o js/dist/bundle.js
- 次に、パッケージ化されたファイルをindex.htmlに導入します。
<script type="text/javascript" src="js/dist/bundle.js"></script>