模塊化Module
Nodejs采用模塊化方式管理和組織代碼,Nodejs的所有功能都存在每個模塊中
1.模塊的了解
1.1什么是模塊
模塊:一個具有特定功能的文件就是模塊
模塊的優(yōu)點:有了模塊,我們就可以風場方式使用這些模塊,因為模塊總是完成特定的功能,如果要修改模塊中的某個功能,那么需要這個模塊文件即可,模塊獨立于每一個文件中,不影響模塊的代碼
模塊之間相互獨立,如果一個模塊引入另一個模塊要使用里面的值,我們就需要在被引入的模塊中暴露這些值
1.2什么是模塊化
模塊化:講一個復雜的程序一局一定的規(guī)則(規(guī)范)封裝成幾個模塊(文件),并進行組合在一起,每個模塊內部數(shù)據(jù)實現(xiàn)私有的,知識向外部暴露一些接口(方法)與外部其他模塊通信
模塊化的進化
//全局開發(fā)模式
//最早期所有的js代碼寫在一個js文件中
function foo(){}
function bar(){}
//造成的問題還就是代碼量過大以后,
//Global全局被污染,很容易導致命名沖突
后來對代碼進行簡單封裝
// 簡單封裝:Namespace 模式 命名空間
var Namespace = {
foo: function(){},
bar: function(){},
}
//減少Global上的變量數(shù)目
//本質就是對象 ,不太安全
因為簡單封裝的不安全,又出現(xiàn)了IIFE模式
//匿名閉包
var Module = (function(){
var foo = function(){
}
return {
foo:foo
}
})()
Module.foo()
//函數(shù)是JavaScript 的Local Scope (局部作用域)
有的時候一個模塊還可能需要依賴其他的模塊,我們就需要在升級一下,將以來的模塊注入
//增強封裝
var Module = (function($){
var $body = $(body);
var foo = function(){
}
return {
foo:foo
}
})($)
//這句就是模塊模式 ,也就是模塊實現(xiàn)的基石
1.3為什么要模塊化
1.降低復雜度
2.提高解耦性
3.部署方便
1.4模塊化的好處
1.避免命名沖突(減少命名空間的污染)
2.更好的分類,按需加載
3.更好的復用性
4.高可維護性
同時模塊化頁導致了很大的問題
1.請求過多
2.依賴模糊
<script src="a.js" ></script>
<script src="b.js" ></script>
<script src="c.js" ></script>
因為造成這些問題所以需要模塊化規(guī)范
1.5模塊的規(guī)范
1.CommonJS(NodeJS)
2.AMD
3CMA
4.ESModule(ES模塊化)
1..5.1模塊的規(guī)范
CommonJS是誕生比較早的,NodeJS就是采用了CommonJS的規(guī)范定義模塊的,但是ComminJS采用的是同步架子啊文件的方式,只適合用于服務端
CommonJS API:不是一個語言,也不是一個平臺,他知識一個標準,主要是對原有的JavaScript標準API進行了增強
CommonJS是一種規(guī)范,NodeJS是對這個對方的實現(xiàn)
在規(guī)范中每一個文件都是一個獨立的模塊
特點:
1.在服務器端:模塊的加載是運行時同步加載
2.在瀏覽器端,模塊需要體檢編譯打包處理
2.NodeJS模塊化
2.1模塊分類
2.1.1 模塊分三種
1.系統(tǒng)模塊
NodeJS開發(fā)團隊已經開發(fā)了很多功能模塊,不需要卸載,直接引入就可以使用
2.第三方模塊
第三方模塊必須先安裝在使用
使用包管理工具npm進行安裝
第三方模塊早npmjs.org中
3.自定義模塊
自己寫一個js文件就是模塊
2.1.2 也有將模塊分兩種的
1.核心模塊(系統(tǒng)模塊)
2.文件模塊(包括第三方模塊和自定義模塊)
2.1.3 如果功能模塊按照關系分
1.主模塊
主模塊就是被Node執(zhí)行的模塊,通常喜歡命名app.js, main.js,index.js
一個項目只能有一個主模塊(也叫入口文件,就是整個項目的啟動模塊)
2.依賴模塊
2.2 內置模塊
NodeJS中的內置了很多模塊,可以直接使用
require用來進行引用,國際慣例,你接受的名字最好和模塊的名字一樣
const http = require('http')
如果特別長可以簡寫
const qs = require('querystring')
內置模塊的引用是無條件的,無路徑的
2.3 自定義模塊
自定義模塊就是自己寫的一個模塊文件
每一個js都是一個模塊,Node.js使用commonjs規(guī)范
定義a模塊
//a.js
var str = '這個是一個字符串'
exports.str = str; //exports是到出(也可以叫暴露)對象
定義b模塊導入a模塊
//b.js
var aa = require('./a.js'');
//此時aa是a模塊中的暴露對象,如果要使用暴露的字符串str
console.log(aa.str)
然后通過node.js執(zhí)行b.js
node b.js
注意點:
1.會發(fā)現(xiàn)require()誰就會執(zhí)行誰,會讓a.js執(zhí)行
2.require()引入的模塊如果有異步語句,不會等死,會獎所有的同步執(zhí)行完畢后執(zhí)行異步語句
3.如果多層引用會把引入文件里的引入執(zhí)行干凈了
4.如果循環(huán)引用,A引用B, B引入A ,Node會很智能的組織好第二次引入
3.Common模塊化
3.1 模塊的組成
在模塊中打印當前模塊的組成信息
console.log(arguments)
console.log(arguments.callee.toString);//打印函數(shù)體
打印的arguments對象
[Arguments] {
'0': {},
'1':{
[Function: require]
resolve: { [Function: resolve] paths: [Function: paths] },
main:
Module {
id: '.',
exports: {},
parent: null,
filename: 'C:\\Users\\HiWin10\\Desktop\\study\\app.js',
loaded: false,
children: [Array],
paths: [Array] },
extensions:[Object: null prototype] {
'.js': [Function],
'.json': [Function],
'.node': [Function]
},
cache:[Object: null prototype] {
'C:\\Users\\HiWin10\\Desktop\\study\\app.js': [Module],
'C:\\Users\\HiWin10\\Desktop\\study\\wu.js': [Module]
}
},
'2':
Module {
id: '.',
exports: {},
parent: null,
filename: 'C:\\Users\\HiWin10\\Desktop\\study\\app.js',
loaded: false,
children: [ [Module] ],
paths:[
'C:\\Users\\HiWin10\\Desktop\\study\\node_modules',
'C:\\Users\\HiWin10\\Desktop\\node_modules',
'C:\\Users\\HiWin10\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules'
]
},
'3': 'C:\\Users\\HiWin10\\Desktop\\study\\app.js',
'4': 'C:\\Users\\HiWin10\\Desktop\\study'
}
打印函數(shù)體
function (exports, require, module,__filename,__dirname){
console.log(arguments.callee.toString())
}
所有的用戶編寫的代碼都自動封裝在一個函數(shù)中,函數(shù)有五個參數(shù),我們就可以在函數(shù)nebula使用五個實參
五個實參
- exports 暴露對象,可以將模塊中的數(shù)據(jù)暴露給引入的地方
- require 引入模塊的函數(shù),用于在一個模塊中引入另一個模塊,并且將子模塊暴露的數(shù)據(jù)賦值給變量
3.module 模塊對象包含了模塊的所有的信息(當前模塊信息)
4.__filename 當前模塊的文件名
5.__dirname 當前模塊所在的路徑
3.2 require函數(shù)(重點)
require(moduleID)函數(shù)英語在當前模塊中加載和使用別的模塊,傳入一個模塊名,返回一個模塊到處對象
'1':{
[Function: require]
resolve: { [Function: resolve] paths: [Function: paths] },
main:
Module {
id: '.',
exports: {},
parent: null,
filename: 'C:\\Users\\HiWin10\\Desktop\\study\\app.js',
loaded: false,
children: [Array],
paths: [Array] },
extensions:[Object: null prototype] {
'.js': [Function],
'.json': [Function],
'.node': [Function]
},
cache:[Object: null prototype] {
'C:\\Users\\HiWin10\\Desktop\\study\\app.js': [Module],
'C:\\Users\\HiWin10\\Desktop\\study\\wu.js': [Module]
}
},
}
./
表示當前目錄,模塊文件路徑的js后綴可以省略
var foo = require('.foo');//當前目錄下的foo.js文件
var foo1 = require('./foo.js')
導入如果不加后綴名,會先找js文件,如果沒有js文件會找json文件,如果沒有會找node文件
注意:
1.引入模塊文件有語法錯誤是會報錯
2.引入模塊不存在時會報錯
3.重復引入模塊只會執(zhí)行一次(返回值會被緩存起來)
4.所有的模塊如果沒有返回值,導入的模塊中將返回空對象
5.刀如自定義模塊必須加'./',因為node.js中查找 模塊默認是在node_modules目錄中查找
6.如果引入第三方模塊直接寫模塊名字就可以了
3.2.1 主模塊main
require有一個屬性main來確定當前模塊是不是主模塊,應該應用程序中,通過node啟動的模塊就是應用模塊,頁叫主模塊
consloe.log(module == require.main)
//ture表示當前模塊是主模塊,false表示當前模塊不是主模塊
3.2.2 require.resole()方法獲取絕對路徑
require.resolve()方法 傳入一個相對路徑參數(shù),返回憑借后的絕對路徑
console.log(require.resolve("./module"))
3.2.3 require.cache屬性
require.cache屬性值是一個對象,緩存了所有已經加載的模塊
console.log(require.cache)
3.3 exports導出對象
exports對象是當前模塊的導出對象,用于導出模塊共有的方法和屬性,別的模塊在通過require函數(shù)導入使用當前模塊時就會獲得當前模塊的exports對象
ps:例子
exports.hello = function(){
console.log("hello world")
}
注意事項:
1.exports是module.exports對象的引用
2.exports不能改指向,只能添加屬性和方法
如果希望更改暴露指向,那么我們就需要使用module.exports進行暴露
var aa = function(){
console.log(11)
}
module.exports = aa;
3.4 module模塊對象(重中之重)
'2':
Module {
id: '.', // 模塊的id ,模塊的名稱, .代表主模塊(其他模塊,id就是模塊的文件路徑)
exports: {}, // 當前模塊導出的內容(重點,其他了解)
parent: null, // 引入當前模塊的父模塊,
path: '' , // 當前文件夾的例子
filename: 'C:\\Users\\HiWin10\\Desktop\\study\\app.js', //當前模塊文件路徑
loaded: false,
children: [ [Module] ], // 子模塊
paths:[ // 默認模塊查找路徑,當前目錄找不到,就當上一層文件夾找
'C:\\Users\\HiWin10\\Desktop\\study\\node_modules',
'C:\\Users\\HiWin10\\Desktop\\node_modules',
'C:\\Users\\HiWin10\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules'
]
},
module對象可以訪問當前模塊的一些相關信息,但最多的用途是替換當前模塊到處的對象
module.exports = {}
例如:默認的到處對象是一個對象,我們可以改變,讓導出的對象為普通的函數(shù)或其他數(shù)據(jù)類型的值
module.exports = function(){
console.log('hello world')
}
module.exports 真正的暴露對象,exports知識對module.exports的引用
3.5 模塊初始化
每一個模塊中的js代碼盡在模塊第一次使用時執(zhí)行一次,并在執(zhí)行過程中初始化模塊的導出對象,之后會將導出對象,之后會將導出的對象緩存起來,被重復利用
4.NodeJS作用域
4.1 作用域
作用域:規(guī)定一個變量和函數(shù)可以使用的范圍
作用域分為兩種:全局作用域和局部作用域
在Node中每一個js文件都是單獨的作用域,因為node的模塊化會將每一個文件中的代碼封裝在一個函數(shù)內部,這和DOM瀏覽器開發(fā)不同,瀏覽器中,var a ,此時的a會自動成為window的屬性,此時js文件和js文件共享作用域,大師Node.js中,每個js文件是獨立作用域不共享,所以Node中每一個文件中的變量都是私有的
例子:
var num = 10
NodeJS會在執(zhí)行之前編譯這個模塊為
function(exports,require,module,__filename,__dirname){
var num = 10
}
4.2暴露(導出)
所以如果讓其他作用域使用本作用域中的值呢,我們采用暴露的方式將值拋出去,也就是暴露共享數(shù)據(jù)
暴露數(shù)據(jù)
module.exports = { num: 10}
使用exports暴露
//test.js
var a = 100;
exports.a = a;
//exports是一個對象,給這個對象添加了一個屬性a,a的值就是本作用域的變量值
此時當我們通過require引入test.js的時候,就可以拿到這個值
//b.js
var test = require('./text.js');
//此時test是引入對象,就是exports對象,如果要獲取a的值應該用
//test.a
console.log(test.a)
注意,模塊叫test問文件,定義接收的變量名也會叫test,其他名字不會報錯,但我們不會這么做
是用module.exports 暴露數(shù)據(jù)
剛才我么使用exports向外暴露一些值,但是我們很不多方便,這個東西必須是exports的屬性,require()導入的時候,返回的是一個對象我們還得去對象里那屬性和方法,如果僅僅只能暴露一個東西,我戲外向外暴露一個類.此時就很不方便
//導出類 Person.js
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
exports.Person = Person;
導入
var Person = require('./Person.js');
//此時的Person是exports對象,如果想使用這個類得Person.Person()使用
var xiaoming = new Person.Person('小明',12,'男')
此時就會發(fā)現(xiàn)使用很不方便
如果js里僅僅只暴露一個東西,我們可以使用module.exports來暴露
這樣module.exports暴露的是什么,那么require()導入返回的還就是什么
module.exports暴露一個東西,exports暴露多個東西
//導出類 Person.js
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
exports.Person = Person;
導入
var Person = require('./Person.js');
//如果采用的module.exports暴露的,這里Person就是暴露的哪個類,所以我們可以直接使用
var xiaoming = new Person('小明',12,'男')
這樣使用的時候就方便很多
4.3定義全局變量
var username = 'xiaoming'
global.name = username
使用時可以不寫global
console.log(name)
5.模塊的共性
所用的慢慢看化都有一個共性,模塊的功能都是把代碼放到一個獨立的函數(shù)中
1.模塊中使用的var定義的變量都是局部變量
2.模塊中定義的函數(shù)也是局部的
3.模塊有一個模塊對象,包含moduleName(模塊名),exports(導出對象)
4.如果模塊中需要,暴露方法或者屬性給外部使用,那么就是像exports對象上添加
5.導入一個模塊使用require('moduleName'),該方法返回的是模塊對象的expots
var aa = require('./a.js')
6.關于路徑與后綴名情況
/表示絕對路徑
./表示相對路徑
如果在引入模塊時不傳入后綴名,會依次趙后綴名為.js,.json,node,都找不到就報錯
若果不寫路徑則認為是模塊內置模塊或各級node_module文件夾中的第三方模塊