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