一牡直、前端模塊化
(1)什么是模塊化救恨?
從廣義的理解來說贸辈,模塊化是一個具有特定功能的對象。
模塊化就是有組織地把一個大文件拆成并互相依賴的小模塊肠槽,一個文件對應(yīng)一個模塊擎淤,模塊的作用域是私有化的,在其內(nèi)部定義的變量或者函數(shù)都是只能在當前的文件使用秸仙,只向外暴露一部分公開的接口嘴拢,如可以修改私有屬性的方法等。
//a.js文件中
var a = 10;
var add = function(val){
return val + a;
};
//在此模塊中定義的變量和函數(shù)是私有的寂纪,在其他文件中不可見
//如果想在多個文件分享某個變量席吴,那么就要定義其為global對象的屬性
global.xx = true;
//此時赌结,xx變量可以被所有文件讀取,但一般不推薦這種寫法孝冒,一般遵循模塊化規(guī)范來寫
const {PI} = Math;
module.exports.getArea = (r)=> PI*r*r; //向外暴露的getArea方法
(2)為什么要模塊化柬姚?
? 隨著程序代碼越寫越多,體系越來越龐大庄涡,不管從開發(fā)角度來說量承,還是從維護的角度來說,都非常地不方便穴店,如果不進行模塊化撕捍,就可能引發(fā)命名沖突,造成不容易復(fù)用代碼泣洞,維護性高忧风。
在此簡單描述一下兩個東西,‘命名沖突’ 和 ‘文件依賴’:
命名沖突:
//a.js文件中
var a = 11;
//b.js文件中
var a = 22;
文件依賴:
//b.js文件依賴于a.js文件球凰,那么標簽的書寫順序必須是:
<script src='a.js' type='text/javascript'></script>
<script src='b.js' type='text/javascript'></script>
類似地狮腿,在開發(fā)中,有時候不可避免地會出現(xiàn) ‘命名沖突’ 或 ‘文件依賴’或其他的問題弟蚀,如果用了模塊化蚤霞,就能比較好地解決這些問題;
使用模塊化的好處:
最大的好處就是大大提高了代碼的可維護性义钉;
其次昧绣,增強了代碼的復(fù)用性;
使用模塊還可以避免函數(shù)名和變量名沖突捶闸;
通俗地來說夜畴,主要就是方便項目的開發(fā)和維護。
(3)怎么樣模塊化删壮?(模塊化的使用)
模塊的定義流程: 1. 定義模塊
? 2.導(dǎo)出模塊
? 3.引用模塊
模塊化的定義規(guī)范有四種: AMD規(guī)范(require.js)贪绘、CMD規(guī)范(Sea.js)、CommonJs的Modules規(guī)范(NodeJs)央碟、ES6模塊化規(guī)范税灌,今天主要只是討論一下CommonJs和ES6兩種模塊化規(guī)范的具體用法,通過閱讀了別的很多資料亿虽,得到如下總結(jié)菱涤。
CommonJs(用于服務(wù)端環(huán)境)
? CommonJs運用于Node.js環(huán)境下(服務(wù)端),換句話說洛勉,Node.js使用了Common.js的規(guī)范粘秆,但是不能理解為common.js是屬于node的,node屬于common.js,這兩種理解都是錯誤的收毫。
? 根據(jù)CommonJs規(guī)范攻走,每個文件就是一個模塊殷勘,有自己的作用域,在一個文件里定義的變量昔搂、函數(shù)都是私有的玲销,對其他文件不可見,CommonJs規(guī)范加載模塊是同步的巩趁,也就是說痒玩,加載完成才可以執(zhí)行后面 的操作,Node.js主要用于服務(wù)器編程议慰,模塊一般都是存在本地硬盤中,加載比較快奴曙,所以Node.js采用CommonJs規(guī)范别凹。
? Common.js的核心思想是通過require方法來同步加載依賴的其他模塊,通過module.exports導(dǎo)出需要暴露的接口洽糟,CommonJS 還可以細分為 CommonJSl 和 CommonJS2炉菲,區(qū)別在于 CommonJSl 只能通過 exports . XX = XX 的方式導(dǎo)出,而 CommonJS2 在 CommonJSl 的基礎(chǔ)上加入了 module.exports = XX 的導(dǎo)出方式坤溃。 CommonJS 通常指 CommonJS2拍霜。
Node.js中Common.js規(guī)范的使用有三種類型:
第一種:內(nèi)置模塊( 內(nèi)置模塊指的是掛載在Node.js全局對象身上的api )
內(nèi)置模塊可以直接使用 ( require中直接書寫模塊名稱 )
格式:const/let/var 變量名 = require( '模塊名稱' )
第二種:自定義模塊,比如:
1)薪介、模塊的定義(創(chuàng)建模塊)
const person = {
id : 1,
name: '張三',
age: 20
}
const fn = function(){}
2)祠饺、模塊的導(dǎo)出: (兩種方式)
//第一種
module.exports = person; //默認導(dǎo)出一個,這種安全性不高
//第二種 批量導(dǎo)出,按需引用
module.exports = {
person,fn
}
3)汁政、模塊的引用
//第一種導(dǎo)出方式的引用
const person = require('./xxx.js')
//第二種導(dǎo)出方式的引用
const {person,fn} require('./xxx/js'); //在自定義模塊引用時道偷,路徑一定要寫對
第三種:第三方模塊(別人封裝好的模塊) 格式:var/let/const 變量名 = require( ''模塊名稱'' )
使用npm/cnpm/yarn安裝所需用的模塊; 在文件中引入;一般在www.npmjs.com會有官方文檔记劈,照著文檔來使用即可勺鸦。
module.export跟exports的區(qū)別
module.exports屬性表示當前模塊對外輸出的接口,其他文件加載該模塊目木,實際上就是讀取module.exports變量换途。module.exports 方法還可以單獨返回一個數(shù)據(jù)類型(String、Number刽射、Object...)军拟,而 exports 只能返回一個 Object 對象;
node為每一個模塊提供了一個exports變量(可以說是一個對象)柄冲,指向 module.exports吻谋。這相當于每個模塊中都有一句這樣的命令 var exports = module.exports; 所有的 exports 對象最終都是通過 module.exports 傳遞執(zhí)行,可以更確切地說现横,exports 是給 module.exports 添加屬性和方法漓拾,一個模塊的對外接口阁最,就是一個單一的值,不能使用exports輸出骇两,必須使用 module.exports輸出速种。
exports.name = 'xiaoming';
exports.age = 20;
console.log(modules.exports); //結(jié)果: {name:'xioaming',age:20}
Commonjs的缺點是,無法直接運行于瀏覽器環(huán)境下低千,必須通過工具轉(zhuǎn)換成標準的ES5配阵。
ES6模塊化(用于瀏覽器環(huán)境)
模塊主要由兩個命令構(gòu)成: export 和 import ; export命令用于規(guī)定模塊的對外接口,import命令用于輸入其他模塊提供的功能示血。
一棋傍、默認導(dǎo)入導(dǎo)出;
1难审、默認導(dǎo)出語法 : export default = { };
//a.js
//定義私有成員
let a = 10;
let b = 20;
let c = 30;
//將私有成員暴露出去瘫拣,供其他模塊使用
export default = {
a,b
}
//c沒有導(dǎo)出,外界就訪問不到
2告喊、使用模塊,導(dǎo)入語法: import 接收名稱 from '某個模塊'麸拄;
import AA from './a.js'
console.log(AA);//輸出結(jié)果: {a: 10, b:20}
注意:
- 在每一個模塊中只允許使用唯一的一次 export default ,否則會報錯
- 在一個模塊中如果沒有向外 export default黔姜,則導(dǎo)入該模塊時 默認輸出 { }
二拢切、按需導(dǎo)出導(dǎo)入
1、按需導(dǎo)出
//aa.js
//按需導(dǎo)出 ,一個模塊中 可以使用 n 多次按需導(dǎo)出
//可以在export的地方定義導(dǎo)出的變量或函數(shù)
export let a = 'AAA';
export let b = 'BBB';
export function say(){
console.log('hi');
}
2秆吵、按需導(dǎo)入: improt { } from '某個模塊'
import {a,b,say} from './aa.js';
console.log(a); // 打印輸出 AAA
console.log(b); // 打印輸出 BBB
console.log(say); // 打印輸出 [Function: say]
//默認導(dǎo)入和按需導(dǎo)入同時使用
import aa,{a,b,say} from 'aa.js';
三淮椰、直接導(dǎo)入并執(zhí)行
如果單純執(zhí)行某個模塊的代碼,并不需要得到模塊中向外暴露的成員時帮毁,可以直接導(dǎo)入并執(zhí)行实苞;
1、比如: 某個模塊中的一段代碼
//e1.js
for (let i = 0; i < 3; i++){
console.log(i);
}
2烈疚、直接導(dǎo)出并執(zhí)行
import './e1.js'
兼容性問題:
node.js不支持ES6模塊化規(guī)范的解決方案
//a.js模塊中
export let name = 'nodeJs';
export let age = 10;
//app.js中導(dǎo)入模塊
import {name,age} from './a.js'
在node環(huán)境下運行app.js會報錯黔牵,SyntaxError: Unexpected token ;
此時爷肝,要么將代碼轉(zhuǎn)換一下猾浦,把ES6規(guī)范轉(zhuǎn)換為commojS規(guī)范,可以用第三方工具解決(推薦使用)
1灯抛、安裝第三方工具金赦,全局安裝babel-cli 和 browserify(已初始化的情況下)
yarn global add babel-cli browserify 或者 npm install babel-cli browserify -g
2、在自己項目目錄下執(zhí)行
yarn add babel-preset-es2015 或者 npm install babel-preset-es2015 --save-dev
3对嚼、在項目根目錄下新建.babelrc文件
{
"presets": [
"es2015"
]
}
4夹抗、寫完代碼后,執(zhí)行
babel src -d lib
5纵竖、運行l(wèi)ib下的app.js即可
node lib\app.js
總結(jié)
CommonJS
1.對于基本數(shù)據(jù)類型漠烧,屬于復(fù)制(淺拷貝)杏愤,會被模塊緩存。同時已脓,在另一個模塊可以對該模塊輸出的變量重新賦值珊楼。
2.對于復(fù)雜數(shù)據(jù)類型,屬于淺拷貝度液。由于兩個模塊引用的對象指向同一個內(nèi)存空間厕宗,因此對該模塊的值做修改時會影響另一個模塊。
3.使用require命令加載某個模塊時就會運行整個模塊的代碼堕担。
4.當加載同一個模塊時已慢,不會再執(zhí)行該模塊,而是得到之前緩存過的值霹购。CommonJS模塊無論加載多少次蛇受,都只會在第一次加載時運行一次,如果以后再加載厕鹃,返回的都是第一次運行的結(jié)果,除非我們自己手動清除系統(tǒng)緩存乍丈。
5.循環(huán)加載時剂碴,屬于加載時執(zhí)行。腳本代碼在require的時候轻专,就會全部執(zhí)行忆矛。某個模塊被"循環(huán)加載",就只輸出已經(jīng)執(zhí)行過的部分请垛,還沒有執(zhí)行的部分不會輸出催训。
ES6模塊
1.ES6 模塊的設(shè)計思想是盡量的靜態(tài)化,使得編譯時就能確定模塊的依賴關(guān)系宗收,es6模塊中的值屬于【動態(tài)只讀引用】漫拭。
2.對于只讀來說,即是不允許修改引入變量的值混稽,import的變量是只讀的采驻,不管是基本數(shù)據(jù)類型還是復(fù)雜數(shù)據(jù)類型。當模塊遇到import命令時匈勋,就會生成一個只讀引用礼旅。等到腳本真正執(zhí)行時,再根據(jù)這個只讀引用洽洁,到被加載的那個模塊里面去取值痘系。
3.對于動態(tài)來說,原始值發(fā)生變化饿自,import加載的值也會發(fā)生變化汰翠。不論是基本數(shù)據(jù)類型還是復(fù)雜數(shù)據(jù)類型龄坪。
4.循環(huán)加載時,ES6模塊是動態(tài)引用奴璃。只要兩個模塊之間存在某個引用悉默,代碼就能夠執(zhí)行。