前言
入了前端坑,哪有不研究下打包工具的。但本人還是拖了好久才把webpack作了較全面的了解,webpack確實(shí)是十分靈活,這得益于其較多的配置項(xiàng),特別在resolve里,能達(dá)成某些奇特的結(jié)果。其外,loader解析文件,plugin注入鉤子,修改打包過(guò)程??梢哉f(shuō),webpack是十分萬(wàn)能,再?gòu)?fù)雜的項(xiàng)目也能夠駕馭。但是,這些復(fù)雜的配置,對(duì)于初學(xué)者是十分不友好的,即使是再簡(jiǎn)單的項(xiàng)目,還是要寫(xiě)出一個(gè)基本的配置項(xiàng),這時(shí)候往往要到官網(wǎng)去復(fù)制粘貼,有時(shí)忘記的某個(gè)配置項(xiàng)的作用,又要重新找(2018/1/15了解到webpack4.0終于要增加默認(rèn)配置了issue)。即使你是較了解webpack了,但還有各種優(yōu)化坑等你,不然開(kāi)發(fā)構(gòu)建慢、打包出來(lái)的js塊十分大,沒(méi)有懶加載、無(wú)法清理緩存。
前一段時(shí)間,parcel出來(lái),號(hào)稱零配置,開(kāi)箱即用(其實(shí)有時(shí)還是要寫(xiě)配置文件,不過(guò)是主要是針對(duì)某些plugin,如:typescript)。除零配置之外,還號(hào)稱在某些情況能大大加快打包速度,理由是針對(duì)一個(gè)文件只會(huì)編譯一次,即使是由多個(gè)plugin,這是使用的AST的好處。還有一個(gè)比較符合我們前端的設(shè)計(jì),入口文件就是html。
使用
node的版本請(qǐng)必須的8以上,在源碼及其他人提供的plugin源碼中,都能容易看到對(duì)node是否8以上的版本的判斷,低于8的話往往是會(huì)報(bào)錯(cuò)的,因?yàn)榛径际褂昧?code>async的語(yǔ)法。
使用parcel,開(kāi)發(fā)時(shí)就基本只需要使用以下兩個(gè)命令:
- 開(kāi)發(fā):
parcel index.html
- 打包:
parcel build index.html
目前官方已針對(duì)各大框架寫(xiě)了例子,還有推薦的各種plugin,地址:awesome-parcel。
各例子其實(shí)也十分簡(jiǎn)潔,沒(méi)有配置項(xiàng),只是在package.json里幫你加好打包需要的plugin。
再?gòu)?fù)雜的例子的話,如vue+typescript的話要自己去加plugin,這個(gè)我剛好找到一個(gè)vue+typescript
想自己從頭敲學(xué)習(xí)的話,可以看這個(gè)all-you-need-to-know-about-parcel
編譯過(guò)程及plugin
對(duì)于如此友好的打包文件,當(dāng)然是要了解下其過(guò)程,還有plugin的編寫(xiě),方便以后確實(shí)需要自己去修改plugin或?qū)懽约旱膒lugin的時(shí)候。
在源碼的src里,cli.js是腳本命令入口,在里面會(huì)導(dǎo)入真正的編譯入口Bundler.js,并構(gòu)建對(duì)象
const bundler = new Bundler(main, command);
打包前bundler.start是一定會(huì)運(yùn)行的,其中就會(huì)加載并注冊(cè)plugin,怎么加載的呢?就是把package.json的plugin依賴都加載了。
let deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
for (let dep in deps) {
if (dep.startsWith('parcel-plugin-')) {
let plugin = await localRequire(dep, this.mainFile);//加載
await plugin(this);//注冊(cè)
}
}
所以我們需要什么plugin,只要把其加入到package.json里就好,雖然可以發(fā)現(xiàn)放在生產(chǎn)依賴?yán)镆残校@些明顯是開(kāi)發(fā)使用的,還是放在開(kāi)發(fā)依賴中比較好,而且優(yōu)先級(jí)也更高,不會(huì)被覆蓋。
接著我們進(jìn)入到plugin,官網(wǎng)內(nèi)置的plugin其實(shí)都在parcel-bundler\src\assets里,只是不叫plugin,他們都繼承于parcel-bundler\src\Asset.js里的Asset,其中parse方法就給用來(lái)自定義編譯內(nèi)容的。
默認(rèn)會(huì)注冊(cè)以下插件(是根據(jù)文件擴(kuò)展名去使用對(duì)應(yīng)的插件):
this.registerExtension('js', './assets/JSAsset');
this.registerExtension('jsx', './assets/JSAsset');
this.registerExtension('es6', './assets/JSAsset');
this.registerExtension('jsm', './assets/JSAsset');
this.registerExtension('mjs', './assets/JSAsset');
this.registerExtension('ml', './assets/ReasonAsset');
this.registerExtension('re', './assets/ReasonAsset');
this.registerExtension('ts', './assets/TypeScriptAsset');
this.registerExtension('tsx', './assets/TypeScriptAsset');
this.registerExtension('coffee', './assets/CoffeeScriptAsset');
this.registerExtension('json', './assets/JSONAsset');
this.registerExtension('yaml', './assets/YAMLAsset');
this.registerExtension('yml', './assets/YAMLAsset');
this.registerExtension('gql', './assets/GraphqlAsset');
this.registerExtension('graphql', './assets/GraphqlAsset');
this.registerExtension('css', './assets/CSSAsset');
this.registerExtension('pcss', './assets/CSSAsset');
this.registerExtension('styl', './assets/StylusAsset');
this.registerExtension('less', './assets/LESSAsset');
this.registerExtension('sass', './assets/SASSAsset');
this.registerExtension('scss', './assets/SASSAsset');
this.registerExtension('html', './assets/HTMLAsset');
這樣就比較明白如何簡(jiǎn)單寫(xiě)出一個(gè)plugin了,首先項(xiàng)目名需要parcel-plugin-
來(lái)作開(kāi)頭。
新建自己的asset文件,并導(dǎo)入繼承Asset的對(duì)象,并編寫(xiě)parse方法;
最后plugin入口index.js:
module.exports = function (bundler) {
bundler.addAssetType('你針對(duì)的文件擴(kuò)展名', require.resolve('你的asset文件'));
};
最后
parcel的熱度確實(shí)比當(dāng)年的webpack高,但目前來(lái)看,其生態(tài)還不完善,即使是官網(wǎng)推薦的plugin,里面的某些功能也是表示在實(shí)驗(yàn)中的,不建議現(xiàn)有比較復(fù)雜的項(xiàng)目從webpack遷移到parcel里。比較簡(jiǎn)單的起始項(xiàng)目推薦去嘗試。