請(qǐng)注意,這是一篇站在完全新手的角度上來(lái)寫(xiě)的文章??赡?a target="_blank">你是一個(gè)后端人員想了解前端工具的使用和概念;也可能你是一個(gè)前端小菜(還在DIV+CSS的世界里掙扎著)。本文比較適合那些以前完全沒(méi)有接觸過(guò)WebPack,而又想使用的朋友。通過(guò)本文你能理解webPack工作原理及做用!(不會(huì)至于看了半天資料還沒(méi)有頭緒?。?/p>
前言:本人是一個(gè)從后端轉(zhuǎn)向前端的程序猿,在此之前對(duì)于前端的印象一直是:HTML + CSS + JS。完全沒(méi)有想過(guò)前端會(huì)發(fā)展的如此的迅速,各種名詞的出現(xiàn):Node、NPM、Grunt、Gulp、Bower、Webpack、Browserify、Yeoman。瞬間讓感覺(jué)到不知道如何下手(好像根本學(xué)不完的樣子)!
先上一張別人的圖,目前的前端工具!
如果你和我一樣,之前對(duì)于前端打包工具的發(fā)展一無(wú)所知,甚至于不知道這些工具出現(xiàn)的必要性。你可以瀏覽此部分的內(nèi)容,如果你不想知道這些或者對(duì)這些并不感興趣,可以直接跳過(guò)此部分。
隨著移動(dòng)互聯(lián)的來(lái)襲,當(dāng)前越來(lái)越多的網(wǎng)站已經(jīng)從單純的網(wǎng)頁(yè)模式,開(kāi)始升級(jí)為webapp模式。它們運(yùn)行在現(xiàn)代的瀏覽器中,使用HTML5、CSS3、ES6等技術(shù)開(kāi)發(fā),已經(jīng)從單一的瀏覽功能轉(zhuǎn)變?yōu)橐粋€(gè)基于瀏覽器的富客戶端。并且webapp通常是一個(gè)SPA(Single Page Application 單頁(yè)面應(yīng)用)。每個(gè)頁(yè)面(View)通過(guò)異步的方式加載,有著良好的用戶體驗(yàn)。但是這樣做的結(jié)果是導(dǎo)致程序初始化和使用的過(guò)程中需要更多、更復(fù)雜的JavaScript代碼來(lái)實(shí)現(xiàn),這就對(duì)前端程序的開(kāi)發(fā)帶來(lái)巨大的挑戰(zhàn)!
隨著程序的復(fù)雜性的增加,項(xiàng)目結(jié)構(gòu)的龐大。把單一js文件按職責(zé)進(jìn)行模塊化劃分。
我們?cè)趯?xiě)頁(yè)面的時(shí)候會(huì)這樣寫(xiě):
<script src="base.js"></script> <script src="utils.js"></script> <script src="vipPush.js"></script>
這是最基礎(chǔ)的JavaScript加載方式,每個(gè)JS的所有方法和屬性都是暴露在window
對(duì)象中的(就像把所有代碼都放在一個(gè)命名空間或者同一個(gè)包下),借助全局對(duì)象,我們就能使用這些屬性和方法。如果更為復(fù)雜的程序會(huì)使用命名空間的概念來(lái)組織這些模塊的接口,比如:YUI
這種開(kāi)發(fā)方式帶來(lái)的弊端:
全局的作用域下容易造成變量的相互沖突(這是一個(gè)很常見(jiàn)的問(wèn)題)
文件只能按照<script>
的書(shū)寫(xiě)順序進(jìn)行加載
開(kāi)發(fā)者要解決各個(gè)模塊和代碼庫(kù)之間的依賴
如果按照此模式進(jìn)行開(kāi)發(fā),長(zhǎng)期下去整個(gè)項(xiàng)目(前端)代碼必定會(huì)混亂不堪
因?yàn)橛辛四K的概念,讓我們的開(kāi)發(fā)變得比較方便。讓我們可以很方便的使用別人的代碼,想要什么功能就加載什么模塊。這樣下去模塊的規(guī)范就變的更重要。目前:通用的JavaScript模塊主要有:
著名的node.js模塊系統(tǒng)就是參照CommonJS規(guī)范來(lái)實(shí)現(xiàn)的。其核心思想就是通過(guò)require
來(lái)進(jìn)行同步加載其它模塊,然后通過(guò)exports
或 module.exports
來(lái)導(dǎo)出需要暴露的接口。
require("module"); require("./file.js"); exports.doStuff = function() {}; module.exports = someValue;
優(yōu)點(diǎn):
服務(wù)器端模塊便于重用
在NPM里有很多功能模塊
簡(jiǎn)單易用
缺點(diǎn):
同步加載的方式注定不能用于客戶端(clients),同步的加載意味著阻塞加載,瀏覽器的加載方式是異步的
不能非阻塞的并行加載多個(gè)模塊
代表:
服務(wù)端 node.js
Browserify,瀏覽器端的 CommonJS 實(shí)現(xiàn),可以使用 NPM 的模塊,但是編譯打包后的文件體積可能很大
AMD(asynchronous Module Definition)意思就是"異步模塊定義",其規(guī)范主要是一個(gè)接口define(id?, dependencies?, factory)
,它采用的是異步加載的方式加載模塊,模塊的加載不影響它后面請(qǐng)語(yǔ)句的運(yùn)行。所有執(zhí)行語(yǔ)句都是在模塊加載完成之后的回調(diào)函數(shù)中執(zhí)行的。
define("module", ["dep1", "dep2"], function(d1, d2) { return someExportedValue; }); require(["module", "../file"], function(module, file) { /* ... */ });
優(yōu)點(diǎn):
適合在瀏覽器環(huán)境中進(jìn)行加載模塊
可以并行多個(gè)模塊
缺點(diǎn):
提高了并發(fā)的成功,代碼的閱讀和書(shū)寫(xiě)比較困難
不符合通用模塊化的思維方式,是一種妥協(xié)的實(shí)現(xiàn)
實(shí)現(xiàn):
CMD(Common Module Definition)規(guī)范與AMD很相似,盡量保持簡(jiǎn)單,并與CommonJs和Node.js的Module規(guī)范保持了很大的兼容性
define(function(require, exports, module) { var $ = require('jquery'); var Spinning = require('./spinning'); exports.doSomething = ... module.exports = ... })
優(yōu)點(diǎn):
依賴就近,延遲執(zhí)行
可以很容易在 Node.js 中運(yùn)行
缺點(diǎn):
依賴 SPM 打包,模塊的加載邏輯偏重
實(shí)現(xiàn):
在ECMAScript2015(es6)中,增加了JavaScript語(yǔ)言層面上的模塊體系定義,其設(shè)計(jì)思想是:盡量的靜態(tài)化,使得編譯時(shí)就能確定模塊的依賴關(guān)系,以及輸入和輸出變量。
import "jquery"; export function doStuff() {} module "localModule" {}
優(yōu)點(diǎn):
容易進(jìn)行靜態(tài)分析
面向未來(lái)的 EcmaScript 標(biāo)準(zhǔn)
缺點(diǎn):
原生瀏覽器端還沒(méi)有實(shí)現(xiàn)該標(biāo)準(zhǔn)
全新的命令字,新版的 Node.js才支持
實(shí)現(xiàn):
把程序所有的文件進(jìn)行模塊化之后,我們還要處理一個(gè)問(wèn)題那就是傳輸問(wèn)題。模塊的化分讓我們可以讓程序變得可以組件化進(jìn)行開(kāi)發(fā),組件雖然被客戶端執(zhí)行,但是依然要由服務(wù)器傳送給客戶端。
關(guān)于組件的傳送有兩個(gè)極端:
每個(gè)組件,一個(gè)HTTP請(qǐng)求
優(yōu)點(diǎn):僅僅傳送依賴項(xiàng)
缺點(diǎn):請(qǐng)求多,負(fù)載高,更慢的啟動(dòng)延遲
所有的組件,一個(gè)HTTP請(qǐng)求
優(yōu)點(diǎn): 更快,更低的延遲
傳送了沒(méi)有必要傳送的東西
讓我在這兩種情況之間做一個(gè)妥協(xié):分塊傳輸
,按需進(jìn)行懶加載,在實(shí)際用某些模塊的時(shí)候進(jìn)行增量的更新,才是比較合理的加載方案。
要實(shí)現(xiàn)這個(gè)功能,需要在編譯打包時(shí)進(jìn)行靜態(tài)的分析、模塊進(jìn)行分批次的打包。那么這個(gè)分批次誰(shuí)來(lái)做呢?
答案就是:WebPack
聯(lián)系客服