Javascript 模塊化管理的來世此生javascript
[TOC]html
模塊管理這個概念其實在前幾年前端度過了刀耕火種年代以後就一直被提起,那麼咱們有思考過這個模塊管理具體指的是什麼東西?什麼樣子的展示形式?歷史由來?如今是什麼樣的一個狀態?前端
直接回想起來的就是 cmd amd commonJS 這三大模塊管理的印象。可是你們清楚 cmd amd commonJS 爲何會出現麼?接下來,咱們就一塊兒來瞅瞅這具體是啥狀況。java
感受本身在每個階段,對於同一個技術的理解都不同。jquery
爲了讓一整個龐大的項目看起來整整齊齊,規規整整而出現的模塊化管理,咱們常見的三種模塊化管理的庫: requireJS、seaJS、commonJS規範 ( 注意,這裏的commonJS不是一個庫,而是一種規範) 逐漸的在咱們平常的開發中出現。 同時依賴於 這三類規範而出現的一些構建工具,但最後都敗給了 webpack 。這是一篇介紹 webpack 基本入門的文章,能夠結合着這篇文章來進行解讀。 《前端之路》之 webpack 4.0+ 的應用構建 webpack
在這個階段中,咱們經常會把很是多複雜的功能 封裝成一個個的函數:git
function f1() {
// todo
}
function f2() {
// todo
}
.
.
.
複製代碼
可是當 整個項目變大了之後,就會遇到不少問題,都是定義的全局變量,造成了比較嚴重的污染,還有可能會出現由於重命名致使的各類問題。因此這些是須要進化的。因此就會進入到模塊化的第二階段: 對象。程序員
到了第二個階段爲了不全局變量的污染,咱們會將單個模塊封裝到一個對象內部。以下:github
const module = {
_number: 10,
f1: () => {
console.log(123)
},
f2: () => {
console.log(456)
},
.
.
.
}
複製代碼
這樣咱們就沒個模塊定義一個對象,在須要的時候直接調用就行了,可是這樣也會存在一個問題 這樣寫的話會暴露所有的對象內部的屬性,
內部狀態能夠被外部改變
. 例如:web
module._number = 100
複製代碼
若是咱們支付相關模塊這樣子來寫的話。咱們隨意的來改變支付的金額,那樣就會出現比較危險的狀況。
後來,聰明的人類就想到了利用 當即執行函數 來達到 不暴露私有成員的目的
const module2 = (function() {
let _money = 100
const m1 = () => {
console.log(123)
}
const m2 = () => {
console.log(456)
}
return {
f1: m1,
f2: m2
}
})()
複製代碼
經過當即執行函數,讓外部根本沒有時間從外部去修改內部的屬性,從而達到必定的防護做用。
以上就是模塊化開發的基礎中的基礎。 沒有庫,沒有規範,一切的維護都是靠人力,一切的創新,都是來源於 解放生產力。
CommonJS 的出發點: JS 沒有完善的模塊系統,標準庫較少,缺乏包管理工具。(雖然這些東西,在後端語言中已是 早就被玩的不想玩的東西了) 伴隨着 NodeJS 的興起,能讓 JS 能夠在任何地方運行,特別是服務端,以達到 也具有 Java、C#、PHP這些後臺語言具有開發大型應用的能力,因此 CommonJS 應運而生。
require
來加載模塊exports
和 module.exports
來暴露模塊中的內容咱們經過編寫一個 Demo 來嘗試寫這個規範
Demo 1 : 經過 module.exports 來導出模塊
// module.js
module.exports = {
name: "zhang",
getName: function() {
console.log(this.name);
},
changeName: function(n) {
this.name = n;
}
};
// index.js
const module = require("./module/index");
console.log(module) // {name: "zhang", getName: ƒ, changeName: ƒ} "commons"
複製代碼
Demo 2 : 經過 exports 來導出模塊
// module1.js
const getParam = () => {
console.log(a);
};
let a = 123;
let b = 456;
exports.a = a;
exports.b = b;
exports.getParam = getParam;
// index.js
const module1 = require("./module/index1");
consoel.log(module1, "commons1") // {a: 123, b: 456, getParam: ƒ} "commons1"
複製代碼
Demo 3 : 同時存在 exports 和 module.exports 來導出模塊
// module2.js
let a = 123;
const getSome = () => {
console.log("yyy");
};
const getA = () => {
console.log(a);
};
exports.getSome = getSome;
module.exports = getA;
// index.js
const module2 = require("./module/index2");
consoel.log(module2, "commons2") // function getA() {...}
複製代碼
總結 : 經過這樣的一個對比的例子就能夠比較清晰的對比出 exports 和 module.exports 的區別: 一、當 exports 和 module.exports 同時存在的時候,module.exports 會蓋過 exports 二、當模塊內部所有是 exports 的時候, 就等同於 module.exports 三、最後 咱們就能夠認定爲 exports 其實就是 module.exports 的子集。
以上就是咱們對於 CommonJS 的一個初級的介紹。 還有一個硬性的規範,這裏咱們只是作一下列舉,就不作詳細的 Demo 演示了。
全部代碼都運行在模塊做用域,不會污染全局做用域;模塊能夠屢次加載,但只會在第一次加載的時候運行一次,而後運行結果就被緩存了,之後再加載,就直接讀取緩存結果;模塊的加載順序,按照代碼的出現順序是同步加載的;
__dirname表明當前模塊文件所在的文件夾路徑,__filename表明當前模塊文件所在的文件夾路徑+文件名;
以上就是關於 CommonJS 規範 相關的介紹,更下詳細的 文檔,能夠查閱 CommonJS 規範 官方文檔。
在 ES2015 標準爲出來以前,最主要的是CommonJS和AMD規範。上文中咱們已經介紹了 CommonJS 規範(主要是爲了服務端 NodeJS 服務),那麼當 ES6標準的出現,爲瀏覽器端 模塊化作了一個很是好的補充。
這裏,咱們仍是比較詳細的介紹下 ES6 的 import export 的系列特性。
export用於對外輸出本模塊(一個文件能夠理解爲一個模塊)變量的接口
Demo 1 export { xxx }
// export/index.js
const a = "123";
const fn = () => window.location.href;
export { fn };
// show/index.js
const ex = require("./export/index");
import x from "./export/index";
import { fn } from "./export/index";
console.log(ex, "export1"); // {fn: ƒ, __esModule: true} "export1"
console.log(x, "export-x"); // undefined "export-x"
console.log(fn, "export-fn"); // function() { return window.location.href; } "export-x"
複製代碼
Demo 2 export default xxx
// export/index1.js
const a = "123";
const fn = () => window.location.href;
export default fn;
// show/index1.js
const ex1 = require("./export/index1");
import x from "./export/index1";
console.log(ex1, "export1");
// {default: ƒ, __esModule: true} "export1"
console.log(x, "export2");
// ƒ fn() {return window.location.href;} "export2"
複製代碼
經過 Demo 1 和 Demo 2 咱們能夠很好的 對比了下 export 和 export default 的區別
export
能夠導出的是一個對象中包含的多個 屬性,方法。 export default
只能導出 一個 能夠不具名的 對象。
import {fn} from './xxx/xxx'
( export 導出方式的 引用方式 ) import fn from './xxx/xxx1'
( export default 導出方式的 引用方式 )
同時,咱們發現 能夠直接使用 require 來引用
這個也能引用其實有點神奇的。可是功能和 import 同樣。(緣由就是我這裏起了 webpack 的 server 相關)
這裏就同上面 Demo 中的例子同樣了得出的結論就是
import {fn} from './xxx/xxx'
( export 導出方式的 引用方式 ) import fn from './xxx/xxx1'
( export default 導出方式的 引用方式 ,能夠不用在乎導出模塊的模塊名)
總結: 以前對於 模塊的導出、引用 的概念都比較的魔幻,此次經過知識的梳理終於搞清楚了。😄
Demo 例子傳送門: 模塊化》》》
Asynchronous Module Definition,中文名是異步模塊。它是一個在瀏覽器端模塊化開發的規範,因爲不是js原生支持,使用AMD規範進行頁面開發須要用到對應的函數庫,也就是大名鼎鼎的RequireJS,實際上AMD是RequireJS在推廣過程當中對模塊定義的規範化的產出。
requireJS主要解決兩個問題:
咱們經過一個 Demo 來介紹下 RequireJS 的語法:
// 定義模塊
define(['myModule'],() => {
var name = 'Byron';
function printName(){
console.log(name);
}
return {
printName:printName
}
})
// 加載模塊
require(['myModule'],function(my){
my.printName();
})
複製代碼
requireJS 語法:
define(id,dependencies,factory)
——id
可選參數,用來定義模塊的標識,若是沒有提供該參數,腳本文件名(去掉拓展名)
——dependencies
是一個當前模塊用來的模塊名稱數組
——factory
工廠方法,模塊初始化要執行的函數或對象,若是爲函數,它應該只被執行一次,若是是對象,此對象應該爲模塊的輸出值。
在頁面上使用require函數加載模塊; require([dependencies], function(){});
require()
函數接受兩個參數: ——第一個參數是一個數組
,表示所依賴的模塊
; ——第二個參數是一個回調函數
,當前面指定的模塊都加載成功後,它將被調用
。加載的模塊會以參數形式傳入該函數,從而在回調函數內部就可使用這些模塊
define(id, deps, factory)
由於CMD推崇一個文件一個模塊,因此常常就用文件名做爲模塊id;
CMD推崇依賴就近,因此通常不在define的參數中寫依賴,而是在factory中寫。
factory有三個參數:
function(require, exports, module){}
一,require
require 是 factory 函數的第一個參數,require 是一個方法,接受 模塊標識 做爲惟一參數,用來獲取其餘模塊提供的接口;
二,exports
exports 是一個對象,用來向外提供模塊接口;
三,module
module 是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法。
demo
// 定義模塊 myModule.js
define(function(require, exports, module) {
var $ = require('jquery.js')
$('div').addClass('active');
});
// 加載模塊
seajs.use(['myModule.js'], function(my){
});
複製代碼
AMD推崇依賴前置,在定義模塊的時候就要聲明其依賴的模塊. CMD推崇就近依賴,只有在用到某個模塊的時候再去require.
你們說:
AMD和CMD最大的區別是對依賴模塊的執行時機處理不一樣,注意不是加載的時機或者方式不一樣
不少人說requireJS是異步加載模塊,SeaJS是同步加載模塊,這麼理解其實是不許確的,其實加載模塊都是異步的,只不過AMD依賴前置,js能夠方便知道依賴模塊是誰,當即加載,而CMD就近依賴,須要使用把模塊變爲字符串解析一遍才知道依賴了那些模塊,這也是不少人詬病CMD的一點,犧牲性能來帶來開發的便利性,實際上解析模塊用的時間短到能夠忽略。
(function(global){
var modules = {};
var define = function (id,factory) {
if(!modules[id]){
modules[id] = {
id : id,
factory : factory
};
}
};
var require = function (id) {
var module = modules[id];
if(!module){
return;
}
if(!module.exports){
module.exports = {};
module.factory.call(module.exports,require,module.exports,module);
}
return module.exports;
}
global.define = define;
global.require = require;
})(this);
複製代碼
使用實例:
define('Hello',function(require,exports,module){
function sayHello() {
console.log('hello modules');
}
module.exports = {
sayHello : sayHello
}
});
var Hello = require('Hello');
Hello.sayHello();
複製代碼
方便組織你的代碼,提升項目的可維護性。一個項目的可維護性高不高,也體現一個程序員的水平,在現在愈來愈複雜的前端項目,這一點尤其重要。
它們功能強大,可是文件體積是個問題,此外還有就是業務有時候可能沒那麼複雜。
移動端頁面,將js注入到html頁面,這樣就不用考慮模塊加載的問題,從而節省了不少的代碼,在實現上也更爲的簡單。 若是是多文件加載的話,須要手動執行文件加載順序,那麼其實最好用庫來進行依賴管理會好一點。
webpack + commonJS + ES6 (import + export )
這樣來 實現模塊管理,實現 較大項目的管理。 好了,模塊化管理就先介紹到這裏了,歡迎一塊兒探討
關於 前端 Javascript 模塊化管理的來世此生 的文章就介紹到這裏了,歡迎一塊兒來論道~
GitHub 地址:(歡迎 star 、歡迎推薦 : )