果你是 JavaScript 的新手,一些像 “module bundlers vs module loaders”、“Webpack vs Browserify” 和 “AMD vs.CommonJS” 這樣的術(shù)語,很快讓你不堪重負(fù)。
JavaScript 模塊系統(tǒng)可能令人生畏,但理解它對 Web 開發(fā)人員至關(guān)重要。
在這篇文章中,我將以簡單的言語(以及一些代碼示例)為你解釋這些術(shù)語。 希望這對你有會有幫助!
好作者能將他們的書分成章節(jié),優(yōu)秀的程序員將他們的程序劃分為模塊。
就像書中的章節(jié)一樣,模塊只是文字片段(或代碼,視情況而定)的集群。然而,好的模塊是高內(nèi)聚低松耦的,具有不同的功能,允許在必要時對它們進(jìn)行替換、刪除或添加,而不會擾亂整體功能。
使用模塊有利于擴(kuò)展、相互依賴的代碼庫,這有很多好處。在我看來,最重要的是:
1)可維護(hù)性: 根據(jù)定義,模塊是高內(nèi)聚的。一個設(shè)計良好的模塊旨在盡可能減少對代碼庫部分的依賴,這樣它就可以獨立地增強(qiáng)和改進(jìn),當(dāng)模塊與其他代碼片段解耦時,更新單個模塊要容易得多。
回到我們的書的例子,如果你想要更新你書中的一個章節(jié),如果對一個章節(jié)的小改動需要你調(diào)整每一個章節(jié),那將是一場噩夢。相反,你希望以這樣一種方式編寫每一章,即可以在不影響其他章節(jié)的情況下進(jìn)行改進(jìn)。
2)命名空間: 在 JavaScript 中,頂級函數(shù)范圍之外的變量是全局的(這意味著每個人都可以訪問它們)。因此,“名稱空間污染”很常見,完全不相關(guān)的代碼共享全局變量。
在不相關(guān)的代碼之間共享全局變量在開發(fā)中是一個大禁忌。正如我們將在本文后面看到的,通過為變量創(chuàng)建私有空間,模塊允許我們避免名稱空間污染。
3)可重用性:坦白地說:我們將前寫過的代碼復(fù)制到新項目中。 例如,假設(shè)你從之前項目編寫的一些實用程序方法復(fù)制到當(dāng)前項目中。
這一切都很好,但如果你找到一個更好的方法來編寫代碼的某些部分,那么你必須記得回去在曾經(jīng)使用過的其他項目更新它。
這顯然是在浪費時間。如果有一個我們可以一遍又一遍地重復(fù)使用的模塊,不是更容易嗎?
有多種方法來創(chuàng)建模塊,來看幾個:
模塊模式用于模擬類的概念(因為 JavaScript 本身不支持類),因此我們可以在單個對象中存儲公共和私有方法和變量——類似于在 Java 或 Python 等其他編程語言中使用類的方式。這允許我們?yōu)橄胍_的方法創(chuàng)建一個面向公共的 API,同時仍然將私有變量和方法封裝在閉包范圍中。
有幾種方法可以實現(xiàn)模塊模式。在第一個示例中,將使用匿名閉包,將所有代碼放在匿名函數(shù)中來幫助我們實現(xiàn)目標(biāo)。(記住:在 JavaScript 中,函數(shù)是創(chuàng)建新作用域的唯一方法。)
例一:匿名閉包
(function () {
// 將這些變量放在閉包范圍內(nèi)實現(xiàn)私有化
var myGrades=[93, 95, 88, 0, 55, 91];
var average=function() {
var total=myGrades.reduce(function(accumulator, item) {
return accumulator + item}, 0);
return '平均分 ' + total / myGrades.length + '.';
}
var failing=function(){
var failingGrades=myGrades.filter(function(item) {
return item < 70;});
return '掛機(jī)科了 ' + failingGrades.length + ' 次。';
}
console.log(failing()); // 掛機(jī)科了次
}());
使用這個結(jié)構(gòu),匿名函數(shù)就有了自己的執(zhí)行環(huán)境或“閉包”,然后我們立即執(zhí)行。這讓我們可以從父(全局)命名空間隱藏變量。
這種方法的優(yōu)點是,你可以在這個函數(shù)中使用局部變量,而不會意外地覆蓋現(xiàn)有的全局變量,但仍然可以訪問全局變量,就像這樣:
var global='你好,我是一個全局變量。)';
(function () {
// 將這些變量放在閉包范圍內(nèi)實現(xiàn)私有化
var myGrades=[93, 95, 88, 0, 55, 91];
var average=function() {
var total=myGrades.reduce(function(accumulator, item) {
return accumulator + item}, 0);
return '平均分 ' + total / myGrades.length + '.';
}
var failing=function(){
var failingGrades=myGrades.filter(function(item) {
return item < 70;});
return '掛機(jī)科了 ' + failingGrades.length + ' 次。';
}
console.log(failing()); // 掛機(jī)科了次
onsole.log(global); // 你好,我是一個全局變量。
}());
注意,匿名函數(shù)的圓括號是必需的,因為以關(guān)鍵字 function 開頭的語句通常被認(rèn)為是函數(shù)聲明(請記住,JavaScript 中不能使用未命名的函數(shù)聲明)。因此,周圍的括號將創(chuàng)建一個函數(shù)表達(dá)式,并立即執(zhí)行這個函數(shù),這還有另一種叫法 立即執(zhí)行函數(shù)(IIFE)。如果你對這感興趣,可以在這里了解到更多。
例二:全局導(dǎo)入
jQuery 等庫使用的另一種流行方法是全局導(dǎo)入。它類似于我們剛才看到的匿名閉包,只是現(xiàn)在我們作為參數(shù)傳入全局變量:
(function (globalVariable) {
// 在這個閉包范圍內(nèi)保持變量的私有化
var privateFunction=function() {
console.log('Shhhh, this is private!');
}
// 通過 globalVariable 接口公開下面的方法
// 同時將方法的實現(xiàn)隱藏在 function() 塊中
globalVariable.each=function(collection, iterator) {
if (Array.isArray(collection)) {
for (var i=0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for (var key in collection) {
iterator(collection[key], key, collection);
}
}
};
globalVariable.filter=function(collection, test) {
var filtered=[];
globalVariable.each(collection, function(item) {
if (test(item)) {
filtered.push(item);
}
});
return filtered;
};
globalVariable.map=function(collection, iterator) {
var mapped=[];
globalUtils.each(collection, function(value, key, collection) {
mapped.push(iterator(value));
});
return mapped;
};
globalVariable.reduce=function(collection, iterator, accumulator) {
var startingValueMissing=accumulator===undefined;
globalVariable.each(collection, function(item) {
if(startingValueMissing) {
accumulator=item;
startingValueMissing=false;
} else {
accumulator=iterator(accumulator, item);
}
});
return accumulator;
};
}(globalVariable));
在這個例子中,globalVariable 是唯一的全局變量。與匿名閉包相比,這種方法的好處是可以預(yù)先聲明全局變量,使得別人更容易閱讀代碼。
例三:對象接口
另一種方法是使用立即執(zhí)行函數(shù)接口對象創(chuàng)建模塊,如下所示:
var myGradesCalculate=(function () {
// 將這些變量放在閉包范圍內(nèi)實現(xiàn)私有化
var myGrades=[93, 95, 88, 0, 55, 91];
// 通過接口公開這些函數(shù),同時將模塊的實現(xiàn)隱藏在function()塊中
return {
average: function() {
var total=myGrades.reduce(function(accumulator, item) {
return accumulator + item;
}, 0);
return'平均分 ' + total / myGrades.length + '.';
},
failing: function() {
var failingGrades=myGrades.filter(function(item) {
return item < 70;
});
return '掛科了' + failingGrades.length + ' 次.';
}
}
})();
myGradesCalculate.failing(); // '掛科了 2 次.'
myGradesCalculate.average(); // '平均分 70.33333333333333.'
正如您所看到的,這種方法允許我們通過將它們放在 return 語句中(例如算平均分和掛科數(shù)方法)來決定我們想要保留的變量/方法(例如 myGrades)以及我們想要公開的變量/方法。
例四:顯式模塊模式
這與上面的方法非常相似,只是它確保所有方法和變量在顯式公開之前都是私有的:
var myGradesCalculate=(function () {
// 將這些變量放在閉包范圍內(nèi)實現(xiàn)私有化
var myGrades=[93, 95, 88, 0, 55, 91];
var average=function() {
var total=myGrades.reduce(function(accumulator, item) {
return accumulator + item;
}, 0);
return'平均分 ' + total / myGrades.length + '.';
};
var failing=function() {
var failingGrades=myGrades.filter(function(item) {
return item < 70;
});
return '掛科了' + failingGrades.length + ' 次.';
};
// Explicitly reveal public pointers to the private functions
// that we want to reveal publicly
return {
average: average,
failing: failing
}
})();
myGradesCalculate.failing(); // '掛科了 2 次.'
myGradesCalculate.average(); // '平均分 70.33333333333333.'
這可能看起來很多,但它只是模塊模式的冰山一角。 以下是我在自己的探索中發(fā)現(xiàn)有用的一些資源:
所有這些方法都有一個共同點:使用單個全局變量將其代碼包裝在函數(shù)中,從而使用閉包作用域為自己創(chuàng)建一個私有名稱空間。
雖然每種方法都有效且都有各自特點,但卻都有缺點。
首先,作為開發(fā)人員,你需要知道加載文件的正確依賴順序。例如,假設(shè)你在項目中使用 Backbone,因此你可以將 Backbone 的源代碼 以<script> 腳本標(biāo)簽的形式引入到文件中。
但是,由于 Backbone 對 Underscore.js 有很強(qiáng)的依賴性,因此 Backbone 文件的腳本標(biāo)記不能放在Underscore.js 文件之前。
作為一名開發(fā)人員,管理依賴關(guān)系并正確處理這些事情有時會令人頭痛。
另一個缺點是它們?nèi)匀粫?dǎo)致名稱空間沖突。例如,如果兩個模塊具有相同的名稱怎么辦?或者,如果有一個模塊的兩個版本,并且兩者都需要,該怎么辦?
幸運的是,答案是肯定的。
有兩種流行且實用的方法:CommonJS 和 AMD。
CommonJS 是一個志愿者工作組,負(fù)責(zé)設(shè)計和實現(xiàn)用于聲明模塊的 JavaScript API。
CommonJS 模塊本質(zhì)上是一個可重用的 JavaScript,它導(dǎo)出特定的對象,使其可供其程序中需要的其他模塊使用。 如果你已經(jīng)使用 Node.js 編程,那么你應(yīng)該非常熟悉這種格式。
使用 CommonJS,每個 JavaScript 文件都將模塊存儲在自己獨立的模塊上下文中(就像將其封裝在閉包中一樣)。 在此范圍內(nèi),我們使用 module.exports 導(dǎo)出模塊,或使用 require 來導(dǎo)入模塊。
在定義 CommonJS 模塊時,它可能是這樣的:
function myModule() {
this.hello=function() {
return 'hello!';
}
this.goodbye=function() {
return 'goodbye!';
}
}
module.exports=myModule;
我們使用特殊的對象模塊,并將函數(shù)的引用放入 module.exports 中。這讓 CommonJS 模塊系統(tǒng)知道我們想要公開什么,以便其他文件可以使用它。
如果想使用 myModule,只需要使用 require 方法就可以,如下:
var myModule=require('myModule');
var myModuleInstance=new myModule();
myModuleInstance.hello(); // 'hello!'
myModuleInstance.goodbye(); // 'goodbye!'
與前面討論的模塊模式相比,這種方法有兩個明顯的好處:
另外需要注意的是,CommonJS 采用服務(wù)器優(yōu)先方法并同步加載模塊。 這很重要,因為如果我們需要三個其他模塊,它將逐個加載它們。
現(xiàn)在,它在服務(wù)器上運行良好,但遺憾的是,在為瀏覽器編寫 JavaScript 時使用起來更加困難。 可以這么說,從網(wǎng)上讀取模塊比從磁盤讀取需要更長的時間。 只要加載模塊的腳本正在運行,它就會阻止瀏覽器運行其他任何內(nèi)容,直到完成加載,這是因為 JavaScript 是單線程且 CommonJS 是同步加載的。
CommonJS一切都很好,但是如果我們想要異步加載模塊呢? 答案是 異步模塊定義,簡稱 AMD。
使用 AMD 的加載模塊如下:
define(['myModule', 'myOtherModule'], function(myModule, myOtherModule) {
console.log(myModule.hello());
});
define 函數(shù)的第一個參數(shù)是一個數(shù)組,數(shù)組中是依賴的各種模塊。這些依賴模塊在后臺(以非阻塞的方式)加載進(jìn)來,一旦加載完畢,define 函數(shù)就會調(diào)用第二個參數(shù),即回調(diào)函數(shù)執(zhí)行操作。
接下來,回調(diào)函數(shù)接收參數(shù),即依賴模塊 - 示例中就是 myModule 和 myOtherModule - 允許函數(shù)使用這些依賴項, 最后,所依賴的模塊本身也必須使用 define 關(guān)鍵字來定義。例如,myModule如下所示:
define([], function() {
return {
hello: function() {
console.log('hello');
},
goodbye: function() {
console.log('goodbye');
}
};
});
因此,與 CommonJS 不同,AMD 采用瀏覽器優(yōu)先的方法和異步行為來完成工作。 (注意,有很多人堅信在開始運行代碼時動態(tài)加載文件是不利的,我們將在下一節(jié)關(guān)于模塊構(gòu)建的內(nèi)容中探討更多內(nèi)容)。
除了異步性,AMD 的另一個好處是模塊可以是對象,函數(shù),構(gòu)造函數(shù),字符串,JSON 和許多其他類型,而CommonJS 只支持對象作為模塊。
也就是說,和CommonJS相比,AMD不兼容io、文件系統(tǒng)或者其他服務(wù)器端的功能特性,而且函數(shù)包裝語法與簡單的require 語句相比有點冗長。
對于同時支持 AMD 和 CommonJS 特性的項目,還有另一種格式:通用模塊定義(Universal Module Definition, UMD)。
UMD 本質(zhì)上創(chuàng)造了一種使用兩者之一的方法,同時也支持全局變量定義。因此,UMD 模塊能夠同時在客戶端和服務(wù)端同時工作。
簡單看一下 UMD 是怎樣工作的:
(function (root, factory) {
if (typeof define==='function' && define.amd) {
// AMD
define(['myModule', 'myOtherModule'], factory);
} else if (typeof exports==='object') {
// CommonJS
module.exports=factory(require('myModule'), require('myOtherModule'));
} else {
// Browser globals (Note: root is window)
root.returnExports=factory(root.myModule, root.myOtherModule);
}
}(this, function (myModule, myOtherModule) {
// Methods
function notHelloOrGoodbye(){}; // A private method
function hello(){}; // A public method because it's returned (see below)
function goodbye(){}; // A public method because it's returned (see below)
// Exposed public methods
return {
hello: hello,
goodbye: goodbye
}
}));
Github 上 enlightening repo 里有更多關(guān)于 UMD 的例子。
你可能已經(jīng)注意到,上面的模塊都不是 JavaScript 原生的。相反,我們已經(jīng)創(chuàng)建了通過使用模塊模式、CommonJS 或 AMD 來模擬模塊系統(tǒng)的方法。
幸運的是,TC39(定義 ECMAScript 的語法和語義的標(biāo)準(zhǔn)組織)一幫聰明的人已經(jīng)引入了ECMAScript 6(ES6)的內(nèi)置模塊。
ES6 為導(dǎo)入導(dǎo)出模塊提供了很多不同的可能性,已經(jīng)有許多其他人花時間解釋這些,下面是一些有用的資源:
與 CommonJS 或 AMD 相比,ES6 模塊最大的優(yōu)點在于它能夠同時提供兩方面的優(yōu)勢:簡明的聲明式語法和異步加載,以及對循環(huán)依賴項的更好支持。
也許我個人最喜歡的 ES6 模塊功能是它的導(dǎo)入模塊是導(dǎo)出時模塊的實時只讀視圖。(相比起 CommonJS,導(dǎo)入的是導(dǎo)出模塊的拷貝副本,因此也不是實時的)。
下面是一個例子:
// lib/counter.js
var counter=1;
function increment() {
counter++;
}
function decrement() {
counter--;
}
module.exports={
counter: counter,
increment: increment,
decrement: decrement
};
// src/main.js
var counter=require('../../lib/counter');
counter.increment();
console.log(counter.counter); // 1
在這個例子中,我們基本上創(chuàng)建了兩個模塊的對象:一個用于導(dǎo)出它,一個在我們需要的時候引入。
此外,在 main.js 中的對象目前是與原始模塊是相互獨立的,這就是為什么即使我們執(zhí)行 increment 方法,它仍然返回 1,因為引入的變量和最初導(dǎo)入的變量是毫無關(guān)聯(lián)的。需要改變你引入的對象唯一的方式是手動執(zhí)行增加:
counter.counter++;
console.log(counter.counter); // 2
另一方面,ES6創(chuàng)建了我們導(dǎo)入的模塊的實時只讀視圖:
// lib/counter.js
export let counter=1;
export function increment() {
counter++;
}
export function decrement() {
counter--;
}
// src/main.js
import * as counter from '../../counter';
console.log(counter.counter); // 1
counter.increment();
console.log(counter.counter); // 2
超酷?我發(fā)現(xiàn)這一點是因為ES6允許你可以把你定義的模塊拆分成更小的模塊而不用刪減功能,然后你還能反過來把它們合成到一起, 完全沒問題。
總體上看,模塊打包只是將一組模塊(及其依賴項)以正確的順序拼接到一個文件(或一組文件)中的過程。正如 Web開發(fā)的其它方方面面,棘手的問題總是潛藏在具體的細(xì)節(jié)里。
將程序劃分為模塊時,通常會將這些模塊組織到不同的文件和文件夾中。 有可能,你還有一組用于正在使用的庫的模塊,如 Underscore 或 React。
因此,每個文件都必須以一個 <script> 標(biāo)簽引入到主 HTML 文件中,然后當(dāng)用戶訪問你的主頁時由瀏覽器加載進(jìn)來。 每個文件使用 <script> 標(biāo)簽引入,意味著瀏覽器不得不分別逐個的加載它們。
這對于頁面加載時間來說簡直是噩夢。
為了解決這個問題,我們將所有文件打包或“拼接”到一個大文件(或視情況而定的幾個文件),以減少請求的數(shù)量。 當(dāng)你聽到開發(fā)人員談?wù)摗皹?gòu)建步驟”或“構(gòu)建過程”時,這就是他們所談?wù)摰膬?nèi)容。
另一種加速構(gòu)建操作的常用方法是“縮減”打包代碼。 縮減是從源代碼中移除不必要的字符(例如,空格,注釋,換行符等)的過程,以便在不改變代碼功能的情況下減少內(nèi)容的整體大小。
較少的數(shù)據(jù)意味著瀏覽器處理時間會更快,從而減少了下載文件所需的時間。 如果你見過具有 “min” 擴(kuò)展名的文件,如 “underscore-min.js” ,可能會注意到與完整版相比,縮小版本非常小(不過很難閱讀)。
除了捆綁和/或加載模塊之外,模塊捆綁器還提供了許多其他功能,例如在進(jìn)行更改時生成自動重新編譯代碼或生成用于調(diào)試的源映射。
構(gòu)建工具(如 Gulp 和 Grunt)能為開發(fā)者直接進(jìn)行拼接和縮減,確保為開發(fā)人員提供可讀代碼,同時有利于瀏覽器執(zhí)行的代碼。
當(dāng)你使用一種標(biāo)準(zhǔn)模塊模式(上部分討論過)來定義模塊時,拼接和縮減文件非常有用。 你真正在做的就是將一堆普通的 JavaScript 代碼捆綁在一起。
但是,如果你堅持使用瀏覽器無法解析的非原生模塊系統(tǒng)(如 CommonJS 或 AMD(甚至是原生 ES6模塊格式)),則需要使用專門工具將模塊轉(zhuǎn)換為排列正確、瀏覽器可解析的代碼。 這就是 Browserify,RequireJS,Webpack 和其他“模塊打包工具”或“模塊加載工具”的用武之地。
除了打包和/或加載模塊之外,模塊打包器還提供了許多其他功能,例如在進(jìn)行更改時生成自動重新編譯代碼或生成用于調(diào)試的源映射。
下面是一些常見的模塊打包方法:
正如前面所知道的,CommonJS以同步方式加載模塊,這沒有什么問題,只是它對瀏覽器不實用。我提到過有一個解決方案——其中一個是一個名為 Browserify 的模塊打包工具。Browserify 是一個為瀏覽器編譯 CommonJS模塊的工具。
例如,有個 main.js 文件,它導(dǎo)入一個模塊來計算一組數(shù)字的平均值:
var myDependency=require(‘myDependency’);
var myGrades=[93, 95, 88, 0, 91];
var myAverageGrade=myDependency.average(myGrades);
在這種情況下,我們有一個依賴項(myDependency),使用下面的命令,Browserify 以 main.js 為入口把所有依賴的模塊遞歸打包成一個文件:
browserify main.js -o bundle.js
Browserify 通過跳入文件分析每一個依賴的 抽象語法樹(AST),以便遍歷項目的整個依賴關(guān)系圖。一旦確定了依賴項的結(jié)構(gòu),就把它們按正確的順序打包到一個文件中。然后,在 html 里插入一個用于引入 “bundle.js” 的 <script> 標(biāo)簽,從而確保你的源代碼在一個 HTTP 請求中完成下載。
類似地,如果有多個文件且有多個依賴時,只需告訴 Browserify 的入口文件路徑即可。最后打包后的文件可以通過 Minify-JS 之類的工具壓縮打包后的代碼。
如果你正在使用 AMD,你需要使用像 RequireJS 或者 Curl 這樣的 AMD 加載器。模塊加載器(與模塊打包工具不同)會動態(tài)加載程序需要運行的模塊。
提醒一下,AMD 與 CommonJS 的主要區(qū)別之一是它以異步方式加載模塊。 從這個意義上說,對于 AMD,從技術(shù)上講,實際上并不需要構(gòu)建步驟,因為異步加載模塊意味著在運行過程中逐步下載那些程序所需要的文件,而不是用戶剛進(jìn)入頁面就一下把所有文件都下載下來。
但實際上,對于每個用戶操作而言,隨著時間的推移,大容量請求的開銷在生產(chǎn)中沒有多大意義。 大多數(shù) Web 開發(fā)人員仍然使用構(gòu)建工具打包和壓縮 AMD 模塊以獲得最佳性能,例如使用 RequireJS 優(yōu)化器,r.js 等工具。
總的來說,AMD 和 CommonJS 在打包方面的區(qū)別在于:在開發(fā)期間,AMD 可以省去任何構(gòu)建過程。當(dāng)然,在代碼上線前,要使用優(yōu)化工具(如 r.js)進(jìn)行優(yōu)化。
就打包工具而言,Webpack 是一個新事物。它被設(shè)計成與你使用的模塊系統(tǒng)無關(guān),允許開發(fā)人員在適當(dāng)?shù)那闆r下使用 CommonJS、AMD 或 ES6。
你可能想知道,為什么我們需要 Webpack,而我們已經(jīng)有了其他打包工具了,比如 Browserify 和 RequireJS,它們可以完成工作,并且做得非常好。首先,Webpack 提供了一些有用的特性,比如 “代碼分割”(code splitting) —— 一種將代碼庫分割為“塊(chunks)”的方式,從而能實現(xiàn)按需加載。
例如,如果你的 Web 應(yīng)用程序,其中只需要某些代碼,那么將整個代碼庫都打包進(jìn)一個大文件就不是很高效。 在這種情況下,可以使用代碼分割,將需要的部分代碼抽離在"打包塊",在執(zhí)行按需加載,從而避免在最開始就遇到大量負(fù)載的麻煩。
代碼分割只是 Webpack 提供的眾多引人注目的特性之一,網(wǎng)上有很多關(guān)于 “Webpack 與 Browserify 誰更好” 的激烈討論。以下是一些客觀冷靜的討論,幫助我稍微理清了頭緒:
當(dāng)前 JS 模塊規(guī)范(CommonJS, AMD) 與 ES6 模塊之間最重要的區(qū)別是 ES6 模塊的設(shè)計考慮到了靜態(tài)分析。這意味著當(dāng)你導(dǎo)入模塊時,導(dǎo)入的模塊在編譯階段也就是代碼開始運行之前就被解析了。這允許我們在運行程序之前移,移除那些在導(dǎo)出模塊中不被其它模塊使用的部分。移除不被使用的模塊能節(jié)省空間,且有效地減少瀏覽器的壓力。
一個常見的問題,使用一些工具,如 Uglify.js ,縮減代碼時,有一個死碼刪除的處理,它和 ES6 移除沒用的模塊又有什么不同呢?只能說 “視情況而定”。
死碼消除(Dead codeelimination)是一種編譯器原理中編譯最優(yōu)化技術(shù),它的用途是移除對程序運行結(jié)果沒有任何影響的代碼。移除這類的代碼有兩種優(yōu)點,不但可以減少程序的大小,還可以避免程序在運行中進(jìn)行不相關(guān)的運算行為,減少它運行的時間。不會被運行到的代碼(unreachable code)以及只會影響到無關(guān)程序運行結(jié)果的變量(Dead Variables),都是死碼(Dead code)的范疇。
有時,在 UglifyJS 和 ES6 模塊之間死碼消除的工作方式完全相同,有時則不然。如果你想驗證一下, Rollup’s wiki 里有個很好的示例。
ES6 模塊的不同之處在于死碼消除的不同方法,稱為“tree shaking”。“tree shaking” 本質(zhì)上是死碼消除反過程。它只包含包需要運行的代碼,而非排除不需要的代碼。來看個例子:
假設(shè)有一個帶有多個函數(shù)的 utils.js 文件,每個函數(shù)都用 ES6 的語法導(dǎo)出:
export function each(collection, iterator) {
if (Array.isArray(collection)) {
for (var i=0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for (var key in collection) {
iterator(collection[key], key, collection);
}
}
}
export function filter(collection, test) {
var filtered=[];
each(collection, function(item) {
if (test(item)) {
filtered.push(item);
}
});
return filtered;
}
export function map(collection, iterator) {
var mapped=[];
each(collection, function(value, key, collection) {
mapped.push(iterator(value));
});
return mapped;
}
export function reduce(collection, iterator, accumulator) {
var startingValueMissing=accumulator===undefined;
each(collection, function(item) {
if(startingValueMissing) {
accumulator=item;
startingValueMissing=false;
} else {
accumulator=iterator(accumulator, item);
}
});
return accumulator;
}
接著,假設(shè)我們不知道要在程序中使用什么 utils.js 中的哪個函數(shù),所以我們將上述的所有模塊導(dǎo)入main.js中,如下所示:
import * as Utils from ‘./utils.js’;
最終,我們只用到的 each 方法:
import * as Utils from ‘./utils.js’;
Utils.each([1, 2, 3], function(x) { console.log(x) });
“tree shaken” 版本的 main.js 看起來如下(一旦模塊被加載后):
function each(collection, iterator) {
if (Array.isArray(collection)) {
for (var i=0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for (var key in collection) {
iterator(collection[key], key, collection);
}
}
};
each([1, 2, 3], function(x) { console.log(x) });
注意:只導(dǎo)出我們使用的 each 函數(shù)。
同時,如果決定使用 filte r函數(shù)而不是每個函數(shù),最終會看到如下的結(jié)果:
import * as Utils from ‘./utils.js’;
Utils.filter([1, 2, 3], function(x) { return x===2 });
tree shaken 版本如下:
function each(collection, iterator) {
if (Array.isArray(collection)) {
for (var i=0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for (var key in collection) {
iterator(collection[key], key, collection);
}
}
};
function filter(collection, test) {
var filtered=[];
each(collection, function(item) {
if (test(item)) {
filtered.push(item);
}
});
return filtered;
};
filter([1, 2, 3], function(x) { return x===2 });
此時,each 和 filter 函數(shù)都被包含進(jìn)來。這是因為 filter 在定義時使用了 each。因此也需要導(dǎo)出該函數(shù)模塊以保證程序正常運行。
我們知道 ES6 模塊的加載方式與其他模塊格式不同,但我們?nèi)匀粵]有討論使用 ES6 模塊時的構(gòu)建步驟。
遺憾的是,因為瀏覽器對 ES6模 塊的原生支持還不夠完善,所以現(xiàn)階段還需要我們做一些補(bǔ)充工作。
下面是幾個在瀏覽器中 構(gòu)建/轉(zhuǎn)換 ES6 模塊的方法,其中第一個是目前最常用的方法:
作為 web 開發(fā)人員,我們必須經(jīng)歷很多困難。轉(zhuǎn)換語法優(yōu)雅的ES6代碼以便在瀏覽器里運行并不總是容易的。
問題是,什么時候 ES6 模塊可以在瀏覽器中運行而不需要這些開銷?
答案是:“盡快”。
ECMAScript 目前有一個解決方案的規(guī)范,稱為 ECMAScript 6 module loader API。簡而言之,這是一個綱領(lǐng)性的、基于 Promise 的 API,它支持動態(tài)加載模塊并緩存它們,以便后續(xù)導(dǎo)入不會重新加載模塊的新版本。
它看起來如下:
// myModule.js
export class myModule {
constructor() {
console.log('Hello, I am a module');
}
hello() {
console.log('hello!');
}
goodbye() {
console.log('goodbye!');
}
}
// main.js
System.import(‘myModule’).then(function(myModule) {
new myModule.hello();
});
// ‘hello!’
你亦可直接對 script 標(biāo)簽指定 “type=module” 來定義模塊,如:
<script type="module">
// loads the 'myModule' export from 'mymodule.js'
import { hello } from 'mymodule';
new Hello(); // 'Hello, I am a module!'
</script>
更加詳細(xì)的介紹也可以在 Github 上查看:es-module-loader
此外,如果您想測試這種方法,請查看 SystemJS,它建立在 ES6 Module Loader polyfill 之上。 SystemJS 在瀏覽器和 Node 中動態(tài)加載任何模塊格式(ES6模塊,AMD,CommonJS 或 全局腳本)。
它跟蹤“模塊注冊表”中所有已加載的模塊,以避免重新加載先前已加載過的模塊。 更不用說它還會自動轉(zhuǎn)換ES6模塊(如果只是設(shè)置一個選項)并且能夠從任何其他類型加載任何模塊類型!
對于日益普及的 ES6 模塊,下面有一些有趣的觀點:
對于 HTTP/1,每個TCP連接只允許一個請求。這就是為什么加載多個資源需要多個請求。有了 HTTP/2,一切都變了。HTTP/2 是完全多路復(fù)用的,這意味著多個請求和響應(yīng)可以并行發(fā)生。因此,我們可以在一個連接上同時處理多個請求。
由于每個 HTTP 請求的成本明顯低于HTTP/1,因此從長遠(yuǎn)來看,加載一組模塊不會造成很大的性能問題。一些人認(rèn)為這意味著模塊打包不再是必要的,這當(dāng)然有可能,但這要具體情況具體分析了。
例如,模塊打包還有 HTTP/2 沒有好處,比如移除冗余的導(dǎo)出模塊以節(jié)省空間。 如果你正在構(gòu)建一個性能至關(guān)重要的網(wǎng)站,那么從長遠(yuǎn)來看,打包可能會為你帶來增量優(yōu)勢。 也就是說,如果你的性能需求不是那么極端,那么通過完全跳過構(gòu)建步驟,可以以最小的成本節(jié)省時間。
總的來說,絕大多數(shù)網(wǎng)站都用上 HTTP/2 的那個時候離我們現(xiàn)在還很遠(yuǎn)。我預(yù)測構(gòu)建過程將會保留,至少在近期內(nèi)。
一旦 ES6 成為模塊標(biāo)準(zhǔn),我們還需要其他非原生模塊規(guī)范嗎?
我覺得還有。
Web 開發(fā)遵守一個標(biāo)準(zhǔn)方法進(jìn)行導(dǎo)入和導(dǎo)出模塊,而不需要中間構(gòu)建步驟——網(wǎng)頁開發(fā)長期受益于此。但 ES6 成為模塊規(guī)范需要多長時間呢?
機(jī)會是有,但得等一段時間 。
再者,眾口難調(diào),所以“一個標(biāo)準(zhǔn)的方法”可能永遠(yuǎn)不會成為現(xiàn)實。
希望這篇文章能幫你理清一些開發(fā)者口中的模塊和模塊打包的相關(guān)概念,共進(jìn)步。
)我們來看看什么是模塊化?
模塊化是一種將系統(tǒng)分離成獨立功能部分的方法,可將系統(tǒng)分割成獨立的功能部分,嚴(yán)格定義模塊接口、模塊間具有透明性。javascript中的模塊在一些C、PHP、java中比較常見:
c中使用include 包含.h文件;php中使用require_once包含.php文件
java使用import導(dǎo)入包
此中都有模塊化的思想。
2)模塊化的優(yōu)缺點:
a>優(yōu)點:可維護(hù)性
1.靈活架構(gòu),焦點分離
2.方便模塊間組合、分解
3.方便單個模塊功能調(diào)試、升級
4.多人協(xié)作互不干擾
可測試性
1.可分單元測試
b>缺點:
性能損耗
1.系統(tǒng)分層,調(diào)用鏈會很長
2.模塊間通信,模塊間發(fā)送消息會很耗性能
......雖然優(yōu)缺點很明顯,但畢竟從協(xié)助和高效角度來講,還是值的學(xué)習(xí)的;下面就直接進(jìn)入正題吧,講一下如何寫簡單的js模塊。(ps:本人是菜鳥,只能寫簡單的)
第一種當(dāng)然是最最簡單的寫法,可能學(xué)習(xí)前端的都會在無形中寫過的---那就是函數(shù)方法:即
模塊就是實現(xiàn)特定功能的一組方法。
只要把不同的函數(shù)(以及記錄狀態(tài)的變量)簡單地放在一起,就算是一個模塊。
原始寫法
上面的函數(shù)m1()和m2(),組成一個模塊。使用的時候,直接調(diào)用就行了。
這種做法的缺點很明顯:"污染"了全局變量,無法保證不與其他模塊發(fā)生變量名沖突,而且模塊成員之間看不出直接關(guān)系。
第二種難度稍微升級,需要面向?qū)ο蟮牟僮髁耍恢饕康氖菫榱私鉀Q上面的缺點,可以把模塊寫成一個對象,所有的模塊成員都放到這個對象里面。
上面的函數(shù)m1()和m2(),都封裝在module1對象里。使用的時候,就是調(diào)用這個對象的屬性。
但是,這樣的寫法會暴露所有模塊成員,內(nèi)部狀態(tài)可以被外部改寫。比如,外部代碼可以直接改變內(nèi)部計數(shù)器的值。
第三種:立即執(zhí)行函數(shù)法;使用"立即執(zhí)行函數(shù)"(Immediately-Invoked Function Expression,IIFE),可以達(dá)到不暴露私有成員的目的。
使用上面的寫法,外部代碼無法讀取內(nèi)部的_count變量。
module1就是Javascript模塊的基本寫法。
接下來的方法都是正對module1的優(yōu)化和加工的寫法;
a):放大模式。
如果一個模塊很大,必須分成幾個部分,或者一個模塊需要繼承另一個模塊,這時就有必要采用"放大模式"(augmentation)。
上面的代碼為module1模塊添加了一個新方法m3(),然后返回新的module1模塊。
b):寬放大模式(Loose augmentation)。
在瀏覽器環(huán)境中,模塊的各個部分通常都是從網(wǎng)上獲取的,有時無法知道哪個部分會先加載。如果采用上一節(jié)的寫法,第一個執(zhí)行的部分有可能加載一個不存在空對象,這時就要采用"寬放大模式"。
與"放大模式"相比,"寬放大模式"就是"立即執(zhí)行函數(shù)"的參數(shù)可以是空對象。
上面的模式就是一些簡單的寫法,期待與各位同學(xué)一起學(xué)習(xí)進(jìn)步;再者感謝阮一峰老師的《Javascript模塊化編程(一):模塊的寫法》文章,讓我受益匪淺。最后再盜一張阮一峰老師的圖片!
程序員HTML5培訓(xùn)教程-html和css的使用方法以及樣式,很好的基礎(chǔ)知識系列,內(nèi)容都是干貨知識點,適合收藏下來!
布局步驟
第一步: 清除默認(rèn)樣式
第二步: 劃分模塊
第三步: 設(shè)置模塊的大小以及位置
第四步: 劃分下一級模塊
html和css
引入網(wǎng)頁頭像
<link rel="shortcut icon" href="img/...ico">
css樣式表的引入方式
css樣式表的引入方式
1、外鏈?zhǔn)?/p>
<link href="" rel="stylesheet">
2、嵌入式
<style></style>
3、行內(nèi)樣式
<div style="width:200px;height:200pxs;"></div>
文件命名以及變量命名
命名規(guī)范
1、嚴(yán)格區(qū)分大小寫
2、可以采用字母數(shù)字下劃線$,數(shù)字不開頭
3、命名語義化
4、可以采用駝峰命名法
清除默認(rèn)樣式
清楚邊距
*{
margin: 0;
padding: 0;
list-style: none;
}
a標(biāo)簽清楚下劃線和顏色
a{
color: black;
text-decoration: none;
}
css中顏色的表示方式
css中顏色的表示方式:
1.預(yù)定義的顏色【關(guān)鍵字顏色】 red pink blue yellow
2.#6位數(shù)的色值 #00-00-00 紅綠藍(lán)
3.rgb(紅,綠,藍(lán)) :rgb([0-255],[0-255],[0-255])
4.rgba(red,green,blue,透明度) :rgba([0-255],[0-255],[0-255],[0-1])
0-1: 0全透明,1不透明
html中的標(biāo)簽和屬性
html:
標(biāo)簽:
按照語法分類:
1.單標(biāo)簽:只有開始標(biāo)簽
meta img a
2.雙標(biāo)簽:有開始標(biāo)簽和結(jié)束標(biāo)簽
<html></html>
3.屬性的語法
語法:
屬性名="屬性值"
屬性名="屬性值1 屬性值2"
注意:
1、標(biāo)簽名和屬性名之間要有空格
2、多個屬性之間要有空格
3、多個屬性值之間要有空格
4.開始標(biāo)簽 標(biāo)簽名后有空格
?
按照標(biāo)簽在頁面中的呈現(xiàn)效果分類:
1、行內(nèi)元素
行內(nèi)元素定義:在一行內(nèi)顯示,只能設(shè)置左右間距,不可以設(shè)置上下間距。
舉例:span del i em b strong a(title="鼠標(biāo)移入時顯示的文字";target=" "(新窗口打開的位置 _self:在本窗口打開;_blank:在新窗口打開) ...
2、塊元素
塊元素定義:可以設(shè)置寬高,獨占一行。
舉例:div 標(biāo)題標(biāo)簽 列表標(biāo)簽 段落標(biāo)簽 ...
3、行內(nèi)塊元素
行內(nèi)塊元素定義:可以設(shè)置寬高,在一行顯示。
舉例:img 【title="鼠標(biāo)移入時顯示的文字" 】 表單控件
元素的轉(zhuǎn)換
塊元素: display:block;
行內(nèi)塊元素:display:inline-block;
行內(nèi)元素: display:inline;
元素的級別
塊元素 > 行內(nèi)塊元素 > 行內(nèi)元素
元素嵌套規(guī)范
1、同一級別可以相互嵌套
2、級別高的元素可以嵌套級別低的元素
3、段落標(biāo)簽只能嵌套行內(nèi)元素
4、a標(biāo)簽不可以嵌套a標(biāo)簽;p不能嵌套p
盒子模型及其問題
四部構(gòu)成:
1、margin 外間距 盒子與盒子之間的距離
2、border 邊框
3、padding 內(nèi)填充(內(nèi)間距) 邊框與內(nèi)容之間的距離。
4、content 內(nèi)容
margin-top margin-right margin-bottom margin-left
margin: 50px; 上 右 下 左
margin: 50px 100px; 上下 左右
margin:0 auto; auto自動
margin: 50px 100px 150px; 上 左右 下
margin: 50px 100px 150px 200px; 上 右 下 左
border: 1px solid red;
border-top \ border-right \border-bottom \ border-left
border-top-width:上邊框的寬度
?
padding:設(shè)置方法同margin
?
content: ;
width : 數(shù)值 百分比 auto
height: 數(shù)值 百分比 auto
?
盒子模型的問題:
1.大部分元素的margin和padding默認(rèn)為0,但有一部分的margin和padding不為0,例如body 標(biāo)題標(biāo)簽(h1-h6)(ul ol il等列表標(biāo)簽) 段落標(biāo)簽
2.想領(lǐng)的兩個塊元素的margin會重合,值會取最大值
3.margin可以為[負(fù)數(shù)] ,padding不可以設(shè)置[負(fù)數(shù)]。
4.行內(nèi)元素margin只有左右,沒有上下
5.如果(1)發(fā)生嵌套關(guān)系的元素,(2)父元素沒有上邊框,(3)上padding ,(4)父元素與子元素之間沒有別的內(nèi)容,此時子元素margin-top就會作用到父元素身上
margin-top的解決方式:
1.用父元素的padding-top代替子元素的margin-top;
2.給父元素添加overflow:hidden;
寬高的設(shè)置和計算
height:auto / 百分比 / px;
width:auto / 百分比 / px;
height:auto; 參照與父元素
width:auto;參照與內(nèi)容
box-sizing:border-box; 將邊框算入盒子內(nèi);
?
一個元素實際的寬高
實際寬度=border-left + padding-left + width +paddint-right + border-right;
實際高度=border-top + padding-top + height + padding-bottom + border-bottom;
浮動
作用:讓塊元素橫排排列
樣式: float:left;從左往右排列
float:right;從右往左排列
原理:讓元素脫離文檔流,讓元素從文檔層浮動到浮動層。
引發(fā)的問題:父元素不設(shè)置高度,子元素都浮動,浮動的子元素?fù)尾婚_父元素。(浮動的元素脫離文檔流)
*解決方式一:給父元素添加 overflow:hidden;(超出部分隱藏)
*解決方式二:在父元素內(nèi)容最后添加擁有清除浮動屬性的元素。
clear:right/left/both ; 別的浮動對它的影響清除掉
例:
.box:after{
content: "";
display:block;
width: 0;
height: 0;
clear:both;
}
*解決方式三:父元素能設(shè)置高度的盡量設(shè)置高度
浮動之后的塊元素參照內(nèi)容:屬性值 auto
定位
定位的元素脫離文檔層,到達(dá)定位層
定位的元素會多出5個樣式:
top right bottom left z-index:999
上 右 下 左 層級(層級越高,離用戶越近)【只能在有定位屬性的元素上才能用】
層級:
z-index:整數(shù);
定位的幾種方式:
1.相對定位:
相對于自身來定位,在文檔層中保留原來的位置
用法:
position:relative;
2.絕對定位:
相對于最近的 定位的 祖先元素 來定位,完全脫離文檔流(其他頂替其位置)
用法:
position:absolute;
+方向值
3.固定定位:
相對于瀏覽器的四條邊,完全脫離文檔流
用法:
position:fixed;
top與bottom同時定義,那個樣式會作用到元素身上的判斷關(guān)系:
top的權(quán)重比bottom的權(quán)重大
left的權(quán)重比right的權(quán)重大
元素作用時:
1.如果是
position:relative;
left:;
margin:;
先作用margin,在作用relative;
2.如果是
position:absolute;
left:;
margin:;
先作用absolute,在作用margin;
定位元素的居中方式:
方法一:
1.水平居中:
position:absolute;
left:50%;
margin-left:-自身長度的一半;
2.垂直居中:
position:absolute;
top:50%;
margin-top:-自身長度的一半;
3.絕對居中:
position:absolute;
left:50%;
top:50%;
margin-left:-自身長度的一半;
margin-top:-自身長度的一半;
方法二:
1.水平居中:
position:absolute;
left:0;
right:0
2D和3D
2D和3D屬性:
1.平移樣式
transform:translate(x,y); 向上為負(fù), 向下為正
transform:translateX(100px);
transform:rotate(180deg) ; (1turn)轉(zhuǎn)一圈
平移 transform:translate() 例子:translate(x,y) translateX()
旋轉(zhuǎn) transform:rotate() 例子rotate(180deg)順時針 -180deg 逆時針
transform:rotate()空格translate();
transform-origin:px px;變換的中心點;
left center;
縮放 transform:scale() 例子:scale(2) 放大為原來的2倍 scale(0.n)縮小為原 來的0.n scale(m,n) x軸m,y軸n
斜切 transform:skew() 例子:skew(45peg) 左拉伸45° skew(45peg,m)
全部 時間
3.過渡的屬性樣式: transition-property: , ;
可以為:屬性的全部樣式
4.過度的總時間: transition-duration:;
5.過渡的時間函數(shù): transition-timing-function:;
linear(勻速) ease(開頭結(jié)尾慢,中間快)
cubic-bezier(1,0.07,0.54,0.21) 貝塞爾曲線
6.延遲 transition-delay:;
?
3d效果:和2d的一樣transition,transform;
prespective:給父元素加prespective(滅點的值)
prespective-origin:x y;滅點的位置 調(diào)整觀察的角度(大多數(shù)情況不設(shè)置)
transform:ratate3d(0-1的值,0-1的值,0-1的值,45deg)
transform:ratateY(45deg)
transform:translate3d(0-1,0-1,px)
?
父元素:transform-style:preserve-3d;
動畫
動畫規(guī)則:
@keyframes 動畫名(隨便給){
(動畫規(guī)則)
from{}
to{}
}
@keyframes 動畫名(隨便給){
(動畫規(guī)則)
0%{}
50%{}
100%{}
}
@keyframes animation1{
from{
background-color:red;
}
to{
background-color:blue;
}
}
?
掛載動畫:將動畫加到元素身上
.元素{
animation:animation1 時間 步數(shù) 時間函數(shù) 延遲時間 次數(shù) ;
}
掛載多個動畫:
.元素{
animation:animation1 時間,animation2 時間,animation1 時間;
其他動畫的相同的可以附件通過animation屬性;
}
animation的樣式
動畫名:animation-name
時間: animation-duration
步數(shù):animation-steps:8;
時間函數(shù):animation-timing-function
延遲: animation-delay
動畫次數(shù): animation-iteration-count:infinite(無限次)/2;
指定下一次動畫是否逆向:animation-direction:alternate(逆向)/ normal(常規(guī));
最后的狀態(tài):animation-fill-mode:backwards(默認(rèn)(保持一開始的狀態(tài)))/forwards(保持當(dāng)前的狀態(tài));
狀態(tài)即指定動畫是否運動: animation-play-state: running(運行)/paused(靜止);
?
元素分類
按照在頁面中的呈現(xiàn)效果:
1.行內(nèi)元素:在一行內(nèi)顯示 ,不可以設(shè)置寬高 :(存放文字)
span a b i strong del
2.行內(nèi)塊元素:在一行內(nèi)顯示,可以設(shè)置寬高:(有縫隙 不常用)
img 表單控件
3.塊元素 :可以設(shè)置寬高,獨占一行
div 標(biāo)題標(biāo)簽(h1-h6) 列表標(biāo)簽(ul-li ol-li dl>dt+dd 段落標(biāo)簽 (p pre))
元素嵌套規(guī)范:
1.同一級別可以相互嵌套
2.級別高的可以嵌套級別低的元素
3.p標(biāo)簽只能嵌套行內(nèi)元素
4.a鏈接不能相互嵌套
元素的轉(zhuǎn)換:
1.塊元素:display:block;
2.行內(nèi)塊元素:display:inline-block;
3.行內(nèi)元素:display:inline;
背景圖片以及瀏覽器內(nèi)核
背景圖
先設(shè)大小,在引background;
background: url('路徑') no-repeat left bottom/contain;
//圖片位置 禁止重復(fù) 位置(top bottom left right)
4. background-repeat:no-repeat,repeat;(無重復(fù))
5. background-repeat:repeat-x(x方向重復(fù))
background-repeat:repeat-y(y方向重復(fù))
background-position:x y;(數(shù)值 方位值(top/bottom left/rightcenter(可以省略)) )
8. padding-box;(默認(rèn))從padding位置開始渲染
9. border-box;從邊框的位置開始渲染
content-box;從內(nèi)容的位置開始渲染
11. padding-box;(默認(rèn))從padding位置結(jié)束渲染
12. border-box;從邊框的位置結(jié)束渲染
content-box;從內(nèi)容的位置結(jié)束渲染
8.可以簡寫:
background:空格隔開;
//漸變開始的方向(默認(rèn)top) 類似25deg(25度)
10.瀏覽器內(nèi)核//背景色漸變
1. /* 標(biāo)準(zhǔn)語法 */
例子:background: linear-gradient(top,#3bbcff,#47eaff);
2. /* 谷歌內(nèi)核 -webkit- */
例子:background: -webkit-linear-gradient(top,#3bbcff,#47eaff);
3. /* 火狐內(nèi)核 -moz- */
例子:background: -moz-linear-gradient(top,#3bbcff,#47eaff);
4. /* 歐鵬內(nèi)核 -o- */
例子:background: -o-linear-gradient(top,#3bbcff,#47eaff);
5. /* IE內(nèi)核 -ms- */
例子:background: -ms-linear-gradient(top,#3bbcff,#47eaff);
?
文件的讀取方法路徑
絕對路徑:從盤符開始的一條完整路徑
相對路徑:兩個文件的位置關(guān)系
邊框的相關(guān)屬性【圓角,邊框形狀】
border-radius:邊框的半徑 設(shè)置圓角 n%或者num像素
border-style:dotted solid double dashed;
上邊框是點狀
右邊框是實線
下邊框是雙線
左邊框是虛線
透明度
透明性的選擇:(整個容器都變)
opacity:;0-1之間的值;
字體
font-family=“ 字體” //字體樣式可以被繼承
鼠標(biāo)移入樣式
span標(biāo)簽
cursor:pointer; 鼠標(biāo)樣式:手型
陰影
box-shadow:x軸偏移量 y軸偏移量 陰影的模糊程度 陰影的大小(0和本身一樣大小) 陰影的顏色;
引入字符圖標(biāo)
引入字符圖標(biāo):
行內(nèi)元素 隨意
span class=“iconfont 圖標(biāo)類名”
可調(diào)節(jié)樣式: 同文字
文檔流
文檔流:
標(biāo)準(zhǔn)情況下 ,頁面元素從左往右 從上往下 依次排列
flex布局(規(guī)范的設(shè)計稿)-彈性布局
容器(父元素)的屬性:【display:flex;】
*flex-direction: 決定主軸方向。
row 主軸在水平方向,從左向右(默認(rèn))。
row-reverse 主軸在水平方向,從右向左
column 主軸在垂直方向,從上到下
column-reverse 主軸在垂直方向,從下到上
*flex-wrap: 決定項目換行
wrap: 項目換行
nowrap: 項目不換行(默認(rèn)值)
wrap-reverse: 項目換行且反轉(zhuǎn)
*justify-content: 決定項目在主軸的對齊方式
flex-start;主軸的起點
flex-end;主軸的終點
center;主軸的中心
space-between;兩端對齊
space-around;項目兩側(cè)距離相等
*align-items:項目在交叉軸上的對齊方式(適用于一根軸線與多跟軸線)
flex-start:交叉軸的起點
flex-end:交叉軸的終點
Center:交叉軸的中心
baseline: 基線對齊(文本底部)
*align-content:定義項目在交叉軸上的對齊方式(僅適用于多根軸線)
flex-start;交叉軸的起點
flex-end;交叉軸的終點
center;交叉軸的中心
space-between;兩端對齊
space-around;兩側(cè)距離相等
子元素(項目)的屬性:
*order:定義項目的排列順序,數(shù)值越小,越靠前,默認(rèn)值為0(可以取負(fù)值)。
*flex-grow:定義項目的放大比例。默認(rèn)值為0,即使存在剩余空間,也不放大。
*flex-shrik:定義項目的縮小比例,默認(rèn)值為1,空間不足,項目縮小;值為0時,空間不足,項目也不縮小.
*flex-basis: 定義項目占據(jù)的主軸空間.默認(rèn)auto或者自己添加像素;
*align-self:定義單個項目在交叉軸的對齊方式.
flex-start:交叉軸的起點
flex-end:交叉軸的終點
Center:交叉軸的中心
滾動條
overflow-x:auto;超出部分在x軸的表現(xiàn)形式。
auto:自動;(如果超出,就自動以滾動條的形式顯示)
去滾動條: 加在具有overflow屬性的元素身上
::-webkit-scrollbar{
height:0;
}
overflow-x: visible|hidden|scroll|auto|no-display|no-content;
值 描述 測試
visible 不裁剪內(nèi)容,可能會顯示在內(nèi)容框之外。 測試
hidden 裁剪內(nèi)容 - 不提供滾動機(jī)制。 測試
scroll 裁剪內(nèi)容 - 提供滾動機(jī)制。 測試
auto 如果溢出框,則應(yīng)該提供滾動機(jī)制。 測試
no-display 如果內(nèi)容不適合內(nèi)容框,則刪除整個框。 測試
no-content 如果內(nèi)容不適合內(nèi)容框,則隱藏整個內(nèi)容。 測試
?
輪播圖
swiper(.js).com
表格
[行] [列]
?
table身上的屬性
table身上的屬性:
border:表格邊框 cellspacing:單元格間的間距
cellpadding:單元格的內(nèi)容與其邊框的內(nèi)邊距
bgcolor:表格的背景顏色 background:表格的背景圖片
width:表格寬度 height:表格高度
border-collaspe:collaspe:邊框合并,不疊加 cellspacing:0:邊框合并,但合并之后的邊框?qū)挾鹊扔?前兩個邊框?qū)挾戎?/p>
caption:表格標(biāo)題
background:表格背景圖
cellspacing:單元格之間的間隙寬度
align:表格的水平對齊方式,通常是left,center,right
表格的標(biāo)題
<caption align="水平對齊方式" valign="標(biāo)題與表格的相對位置"></caption>
單元格【tr】【td】
width:單元格寬度height:單元格高度
align:單元格內(nèi)文本的對齊方式,通常是左,中,右 left,center,right
valign:單元格內(nèi)文本的對齊方式,通常是上,中,下 top,middle,bottom
nowrap:在為設(shè)置單元格寬度時,當(dāng)文本長度寬于單元格寬度,將要換行時,該標(biāo)簽會使其不換行
?
<tr align="center" valign="bottom">
<td align="center" nowrap>手機(jī)空中免費充值</td>
<td width="100px">IP卡</td>
<td width="100px" bgcolor="#006400" valign="top">網(wǎng)游</td>
</tr>
表格的跨行與跨列【td】
rowspan:跨行標(biāo)簽,表示跨了多少行
colspan:跨列標(biāo)簽,表示跨了多少列
表格標(biāo)簽拓展及其屬性
thead:定義表格的表頭。
tbody:定義表格主體(正文)。
tfoot:定義表格的頁腳(腳注或表注)。
colgroup:標(biāo)簽用于對表格中的列進(jìn)行組合,以便對其進(jìn)行格式化。
注意:不管thead、tbody、tfoot的代碼先后順序如何,html顯示時,始終是先顯示thead,再顯示tbody,最后顯示tfoot。
1、<thead> 內(nèi)部必須擁有 <tr> 標(biāo)簽!
2、<tfoot> 元素內(nèi)部必須包含一個或者多個 <tr> 標(biāo)簽。
3、<tbody> 元素內(nèi)部必須包含一個或者多個 <tr> 標(biāo)簽。
4、必須在 table 元素內(nèi)部使用這些標(biāo)簽。
5、當(dāng)不同行間的單元格合并時各單元格所在的行不要加tbody標(biāo)簽。
標(biāo)題欄
《tr》<th></th>《/tr》 用法和td相似 知識自動將單元格內(nèi)容以粗體顯示
表單控件表單標(biāo)簽
<form action=" " method=" ">
action:表單信息提交的位置;
method:提交的方式
get:地址欄,信息量少,安全性低
post:信息量多,比較安全
1.輸入文本【輸入框】:
用戶名:<input type="text" placeholder="請輸入用戶名" maxlength="10" value=" " name="username" class="">
placeholder:默認(rèn)提示文本;
maxlength:規(guī)定輸入的最大字符數(shù)
name:本文本框的名字,與后臺進(jìn)行數(shù)據(jù)交互用
class:定義本文本框的樣式,相當(dāng)于盒子
placeholder下的縮進(jìn)
text-indent:2em;縮進(jìn)
2.輸入密碼【密碼框】:
密碼:<input type="password" placeholder="請輸入密碼" maxlength="10" value=" " name="psw" class="">
3.單選按鈕[name的值必須相同]:
請選擇你的性別:
<label for="man"> [label實現(xiàn)點什么就選中 ,for中的值和id中的值相同]
男:<input type="radio" name="sex" id="man" checked> //checked默認(rèn)選項
</label>
<label for="woman">
女:<input type="radio" name="sex" id="woman">
</label>
4.多選按鈕[name的值必須相同]:
請選擇你喜歡的音樂:
搖滾:<input type="checkbox" checked>
搖滾:<input type="checkbox" checked>
搖滾:<input type="checkbox" checked>
5.下拉列表【下拉框】:
選擇你的學(xué)歷:
<select name="" id="">
<option value="">學(xué)士</option>
<option value="">博士</option>
<option value="">碩士</option>
</select>
6.上傳文件:
選擇你的照片:
<input type="file">
7.留言文本空間:
<textarea name="" id="" rows="" col="">
</textarea>
8.用戶是否允許重新設(shè)置textarea大小css屬性:
resize: none/both/vertical/horizontal;不允許/上下允許拖動/只能在垂直方向拖動/只能在水平方向 拖動
9.重置按鈕:
<input type="reset">
10.提交按鈕:
<input type="submit">
11.自定義按鈕:
<input type="button" value="按鈕">
<button>搜索</button>
12.顏色:
<input type="color">
13.時間日期:
年月:<input type="month">
年周:<input type="week">
時分:<input type="time">
年月日:<input type="date">
年月時分:<input type="datetime-local">
14.驗證
<input type="email"> 郵箱驗證
<input type="tel" autofocus> 電話
15. autofocus 自動獲取焦點
</form>
文本模型
文本換行
使非中日韓文本換行
word-break: break-all ;
文本禁止換行
white-space:nowrap;
單行文本溢出部分以省略號顯示
overflow: hidden;(放文本的容器)
text-overflow: ellipsis;
多行文本溢出
音頻視頻標(biāo)簽
音頻標(biāo)簽
<audio src="" controls loop autoplay></audio>
controls 空間向用戶顯示:
loop 循環(huán)播放
autoplay當(dāng)前頁面加載完自動播放
視頻標(biāo)簽
<video src="" controls loop autoplay></video>
H5語義化標(biāo)簽
<header>頭部</header>
<nav>導(dǎo)航</nav>
<aside>側(cè)導(dǎo)航<aside>
<section>頁面中的某一部分</section>
<main>主體</main>
<footer>底部</footer>
meta標(biāo)記【簽】
name="關(guān)鍵字" cantent="內(nèi)容"
<mate http-equiv="Refresh" content="10";url="跳轉(zhuǎn)路徑"> //每10s刷新一次并且跳轉(zhuǎn)到跳轉(zhuǎn)路徑知識的文件
bgsound標(biāo)簽
<bgsound src="路徑" loop="播放次數(shù)">
body屬性
對文字操作的標(biāo)簽
列表標(biāo)簽
2. <li>
<li>
</ul>
4. <li>
<li>
</ol>
a標(biāo)簽
<a href="路徑 " title="鼠標(biāo)移入時顯示的文字" target=" "(新窗口打開的位置 _self:在本窗口打開;_blank:在新窗口打開;_parent:在當(dāng)前窗口的父窗口打開鏈接;_top:在整個瀏覽器窗口打開) ...
[字符實體]常用的轉(zhuǎn)義字符
顯示結(jié)果 描述 實體名稱
空格
< 小于號
大于號
& 和號
" 引號
' 撇號 (IE不支持)
¢ 分(cent)
£ 鎊(pound)
¥ 元(yen)
歐元(euro)
§ 小節(jié)
? 版權(quán)(copyright)
? 注冊商標(biāo)
? 商標(biāo)
× 乘號
÷ 除號
選擇器
分類
css選擇器
1.通用選擇器:
*{}//選擇所有的標(biāo)簽
2.群組選擇器:
E1,E2,E3..{}//選擇E1 E2 E3
3.標(biāo)簽選擇器
標(biāo)簽名{}
4.類名選擇器:
.類名{}
5.后代選擇器
.E1 .E2{} //選擇E1 的后代E2
6.交叉選擇器
標(biāo)簽名.類名{}
7.id選擇器
例如 創(chuàng)建id
<div id=“box”></div>
#id名{} //選擇頁面中id為**的標(biāo)簽
8.偽類選擇器:
鼠標(biāo)移入狀態(tài)
E:hover{ } E元素選擇鼠標(biāo)移入狀態(tài)
E:hover .子類{ } 選擇e元素下鼠標(biāo)移入時子類的變化
獲取焦點,用于表單的輸入
E: focus{
outline: none;
}
9.偽結(jié)構(gòu)選擇器:
E:first-child{} 作為子元素的第一個孩子的E標(biāo)簽
E:last-child{} 作為子元素的最后第一個孩子的E標(biāo)簽
E: nth-child(n){} //作為子元素的第n個孩子的E標(biāo)簽
E: nth-last-child(n) 作為子元素的倒數(shù)第n個孩子的E標(biāo)簽
E:first-of-type{} 作為子元素的同類型的第一元素
E:last-of-type{} 作為子元素的同類型中的最后一個元素
E: nth-of-type(n) 作為子元素的同類型中的第n個元素
E: nth-last-of-type(n) 作為子元素的同類型中的倒數(shù)第n個元素
(n)n可以以為num/even(偶數(shù))/odd(奇數(shù))/3n(3的倍數(shù))
例子:5.15/偽結(jié)構(gòu)選擇器
10.偽元素選擇器:
::after{} 在元素之后加入一個
::before{
content:“內(nèi)容之前”;
color:;
} 在元素之前
屬于行內(nèi)元素
::after{} ::before{}偽元素 content:""; 樣式必須寫
?
11.子類選擇器
相鄰兄弟選擇器
E1+E2{} 選擇E1的下一個兄弟元素E2(不能選中上一個兄弟元素)
div.box>a+img a和img統(tǒng)計
子類選擇器
E1>E2{} 選擇E1的子類元素E2
例子:div.box>div.item{$}*20
div.box>a>img
12.屬性選擇器
[屬性名]{} 選擇所有擁有屬性為 屬性名 的元素
[屬性名=“value”]{} 選擇擁有屬性名的屬性 且屬性值為value
E[屬性名=“value”]{} 選擇擁有屬性名的屬性 且屬性值為value的E元素
E[屬性名~=“value”]{} 選擇擁有屬性名的屬性 并且屬性值一個或者多個,其中一個屬性值為value的E元素
E[value^=“1”]{} 選擇擁有 value的屬性 并且屬性值一個或者多個,其中一個屬性值以 1 開頭的E元素
E[value$=“1”]{} 選擇擁有 value的屬性 并且屬性值一個或者多個,其中一個屬性值以 1 結(jié)尾的E元素
E[value*=“1”]{} 選擇擁有 value的屬性 并且屬性值一個或者多個,其中一個屬性值包含 1 的E元素
例子:屬性選擇器
選擇器的優(yōu)先級
宗旨:越具體的優(yōu)先級越高
id (100 ) > class( 10 )> 標(biāo)簽名( 1)
.box .son{ } 10+10=20
abcde優(yōu)先級(e為個位):
a:行內(nèi)樣式
b:id選擇器
c:類名選擇器 偽類選擇器(:hover) 屬性選擇器
d:標(biāo)簽選擇器 偽元素選擇器 (::after)
e:通用選擇器有一個
選擇有中有一個abcde在其位置+1
移動端布局步驟
?
視口:視覺視口,布局視口,理想視口
em:當(dāng)前字體的倍率 100px=10em
rem:html字體的倍率
移動端窗口 375*667
html{
font-size:0.5rem;
}
.box{
width: 750rem; //375px=750rem*0.5px ; 100px=1rem
height: 1334rem;
}
未完待續(xù),感謝關(guān)注好程序員前端教程分享!
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。