目錄
- 前端工程戶核心技術之模塊化
- 前端模塊化的進化過程
- commonjs規范介紹
- commonjs規范示例
- commonjs模塊打包
- amd規范、cmd規范
- 前端工程化關鍵技術之npm+webpack原理
前端工程戶核心技術之模塊化
前端模塊化是一種標準,不是實現。commonjs是前端模塊化的標準,而nodejs實現了一套commonjs的規范。
什么是前端模塊化:將復雜程序根據規范拆分成若干個模塊,每個模塊間組織是有邏輯的,一個模塊包括輸入和輸出,每個模塊內部實現時私有的,對外暴露接口與其他模塊通信,而不是直接調用的關系。這其實就是一種典型的面向對象編程思想。一個html頁面可以引用的script標簽包括:腳本和模塊
腳本和模塊的區別:
原來我們所有的業務邏輯代碼只能寫在index.js里,但用了模塊化標準以后,就可以對他進行拆分了,可以把他拆分為一個入口文件entry.js,這個入口文件去引用若干個模塊,然后把他們組織起來進行調用,瀏覽器去調用時,主要訪問入口文件,再由入口文件調用其他模塊化文件,組裝成一個業務邏輯。腳本和模塊化最典型的區別就是,通過模塊化拆分后,我們可以更清楚的看到整個業務的邏輯,如果只有一個index.js你并不知道源碼里面在干什么。
前端模塊化的進化過程
定義的函數是掛載在window上的,如果有一個文件夾里也定義api函數,會引發全局命名空間沖突
commonjs規范介紹
commonjs是nodejs默認模塊化規范,每個文件就是一個模塊,有自己的作用域,可以維護自己的私有變量,node中cjs模塊加載采用同步加載方式,必須等這個模塊加載完了再去執行后面的代碼,通過require加載模塊,通過exports或者module.exports輸出模塊。
commonjs規范特點:
- 所有代碼都運行在模塊作用域,沒有js腳本概念,寫一個js文件就是一個模塊,所以不會污染全局作用域。
- 模塊可以多次加載,第一次加載時會運行模塊,模塊輸出結果會被緩存,再次加載時,會從緩存結果中直接讀取模塊輸出結果。
- 模塊加載的順序,按照其在代碼中出現的順序
- 模塊輸出的值是值的拷貝,類似iife方案中的內部變量
commonjs規范示例
每個功能創建一個文件,如api文件:
const handle = require('./handle')
function api(){return {code: 0,data:{a: 1,b: 2,}}
}
module.exports = {api,handle
}
handle.js文件:
function handle(data, key){return data.data[key]
}module.exports = handle;
sum.js文件:
function sum(a, b){return a+b;
}module.exports = sum;
然后在entry.js中引入各文件,執行相關業務邏輯:
首先,我們需要有一個主模塊(即入口文件entry.js),在主模塊中,我們會使用require加載模塊,require加載模塊時會把模塊變成module對象,module對象中有一個load方法,通過load方法進行模塊加載,在加載過程中,他會在模塊的外層包一層,把原來的模塊變成自適應函數,我們寫的代碼(比如寫在api.js中的代碼)變成函數里面的內容了,他會向自適應函數傳入一些變量(require、module、exports、_filename、_dirname),這就是為什么在node里可以直接使用require的原因。使用module.exports輸出模塊時,最終輸出結果會被緩存到module cache map中,這是一個鍵值對,鍵是module path+module name,值是module.exports。commonjs輸出模塊時,module.exports只能輸出一個結果
commonjs模塊打包
安裝browserify:npm install browserify -g
打包命令:browserify module_test/cjs/entry.js -o dist/bundle.js
??當存在多個entry.js模塊時,每個entry.js模塊都需要單獨打包
browserify打包原理:
- 本質還是通過自執行函數實現模塊化
- 將每個模塊編號,存入一個對象,每個模塊標記依賴模塊
- 實現了require方法,核心是通過call方法調用模塊,并傳入require、module、exports方法,通過module存儲模塊信息,通過exports存儲模塊輸出信息
amd規范、cmd規范
amd規范采用非同步(異步)加載模塊,允許指定回調函數。node模塊通常位于本地,加載速度快,所以適用于同步加載,但是在瀏覽器中,如果用同步加載會阻塞模塊渲染(整個頁面的渲染),所以在瀏覽器環境下,模塊需要請求獲取,適用于異步加載,所以誕生了amd規范,用來做異步加載,require.js是amd的一個具體實現庫,amd、cmd目前很少使用,因為目前我們主要使用node和瀏覽器開發,分別使用commonjs和esmoudle。
cmd規范整合了commonjs和amd的優點,模塊加載是異步的,cmd專門用于瀏覽器端,sea.js是cmd規范的一個實現,amd和cmd最大的問題是沒有通過語法升級解決模塊化,他們定義模塊還是通過調用js方法來生成模塊,這種方式利于我們快速應用,但是他沒有辦法對模塊化進行規模化的應用,因為他們沒有實現標準的語法規范
ESMoudle規范設計理念是希望在編譯時就確定模塊以來關系及輸入輸出,commonjs和amd必須在運行時才能確定依賴和輸入、輸出,ESMoudle通過import加載模塊,通過exports輸出模塊
commonjs和ESMoudle規范的對比:
- commonjs模塊輸出的是值的拷貝,也就是說commonjs模塊內部的值你想改是改不了的,但是es6模塊輸出的是值的引用
使用es6定義test.js文件:
export let a = 1;
export function plus() {a++;
}
定義entry.js文件:
import {a, plus} from './test.js'
console.log(a);//1
plus();
console.log(a);//2
使用commonjs定義test.js:
let a = 1;
exports.a = a;
exports.plus = function(){a++;
}
// 上述代碼等同于:
// module.exports={
// a:1,// 這個a的值是直接拷貝過來的
// plus// plus里加的是模塊里的a
// }
定義entry.js文件:
const { a, plus} = require(./test.js);
console.log(a);// 1
plus();
console.log(a);// 1
如果想要獲取模塊里的a,需要使用get方法,在commonjs定義的test.js中定義get方法:
let a = 1;
exports.a = a;
exports.plus = function(){a++;
}
exports.get = function(){return a;
}
在entry.js文件中調用get:
const { a, plus, get} = require(./test.js);
console.log(a);// 1
plus();
console.log(a);// 1
console.log(get());// 2
- commonjs模塊是運行時加載,es6模塊是編譯時輸出接口,在編譯時就能確定導入哪些模塊,輸出哪些模塊
- commonjs是單個值導出,es6可以導出多個
- commonjs模塊為同步加載,es6支持異步加載,導出的是一個promise
- commonjs的this是當前模塊的輸出值,es6的this是undefined
- commonjs和es6的語法不同
script腳本和模塊對比:模塊具備更高的開發效率,可以將復雜的代碼拆分成若干簡單的代碼,代碼可讀性強,復用高效,但是模塊在加載過程中會損耗性能,因為模塊文件很多,會使得加載速度變得更慢,而腳本具有更高的頁面性能。模塊在瀏覽器中運行會存在兼容性問題,要特別注意。所以在瀏覽器中運用模塊化存在一些局限性,比如瀏覽器缺乏模塊管理能力,模塊分散在各個項目中,模塊性能加載慢,無法在大型項目中直接使用,這兩個問題是npm(解決模塊管理能力問題)和webpack(解決模塊加載慢問題)核心解決的問題