詳解webpack 多頁(yè)面/入口支持&公共組件單獨(dú)打包
webpack系列目錄
webpack 系列 三:webpack 如何集成第三方j(luò)s庫(kù)
webpack 系列 四:webpack 多頁(yè)面支持 & 公共組件單獨(dú)打包
webpack 系列 五:webpack Loaders 模塊加載器
webpack 系列 六:前端項(xiàng)目模板-webpack+gulp實(shí)現(xiàn)自動(dòng)構(gòu)建部署
基于webpack搭建純靜態(tài)頁(yè)面型前端工程解決方案模板, 最終形態(tài)源碼見(jiàn)github: https://github.com/ifengkou/webpack-template
正文
本篇主要介紹:如何自動(dòng)構(gòu)建入口文件,并生成對(duì)應(yīng)的output;公共js庫(kù)如何單獨(dú)打包。
多入口文件,自動(dòng)掃描入口。同時(shí)支持SPA和多頁(yè)面型的項(xiàng)目
公共js庫(kù)如何單獨(dú)打包。
上一篇示例,主要介紹如何集成第三方j(luò)s庫(kù)到項(xiàng)目中使用,如jquery。示例的入口只有一個(gè)index,而且是將公共js庫(kù)連同page.js一起打包到output.js中。那么在開(kāi)發(fā)中會(huì)出現(xiàn),每新增一個(gè)頁(yè)面模塊,就需要修改webpack.config.js配置文件(增加一個(gè)入口),而且如果用到的第三方庫(kù)比較多,這樣也容易導(dǎo)致jquery,React等代碼庫(kù)重復(fù)被合并到打包后的js,導(dǎo)致js體積過(guò)大,頁(yè)面加載時(shí)間過(guò)長(zhǎng)
基礎(chǔ)結(jié)構(gòu)和準(zhǔn)備工作
以下示例基于上一篇進(jìn)行改進(jìn),上一篇項(xiàng)目源碼
目錄結(jié)構(gòu)說(shuō)明
. ├── package.json # 項(xiàng)目配置 ├── src # 源碼目錄 │ ├── pageA.html # 入口文件a │ ├── pageB.html # 入口文件b │ ├── css/ # css資源 │ ├── img/ # 圖片資源 │ ├── js # js&jsx資源 │ │ ├── pageA.js # a頁(yè)面入口 │ │ ├── pageB.js # b頁(yè)面入口 │ │ ├── lib/ # 沒(méi)有存放在npm的第三方庫(kù)或者下載存放到本地的基礎(chǔ)庫(kù),如jQuery、Zepto、avalon │ ├── pathmap.json # 手動(dòng)配置某些模塊的路徑,可以加快webpack的編譯速度 ├── webpack.config.js # webpack配置入口
一:自動(dòng)構(gòu)建入口
官方多入口示例
webpack默認(rèn)支持多入口,官方也有多入口的示例。配件文件webpack.config.js如下
//已簡(jiǎn)化 var path = require("path"); module.exports = { entry: { pageA: "./pageA", pageB: "./pageB" }, output: { path: path.join(__dirname, "js"), filename: "[name].bundle.js", chunkFilename: "[id].chunk.js" } }
每新增一個(gè)頁(yè)面就需要在webpack.config.js的entry 中增加一個(gè) pageC:"./pageC",頁(yè)面少還好,頁(yè)面一多,就有點(diǎn)麻煩了,而且配置文件,盡可能不改動(dòng)。那么如何支持不修改配置呢?
自動(dòng)構(gòu)建入口函數(shù)
entry實(shí)際上是一個(gè)map對(duì)象,結(jié)構(gòu)如下{filename:filepath},那么我們可以根據(jù)文件名匹配,很容易構(gòu)造自動(dòng)掃描器:
npm 中有一個(gè)用于文件名匹配的 glob模塊,通過(guò)glob很容易遍歷出src/js目錄下的所有js文件:
安裝glob模塊
$ npm install glob --save-dev
修改webpack.config.js 配置,新增entries函數(shù),修改entry:entries(),修改output的filename為"[name].js"
//引入glob var glob = require('glob') //entries函數(shù) var entries= function () { var jsDir = path.resolve(srcDir, 'js') var entryFiles = glob.sync(jsDir + '/*.{js,jsx}') var map = {}; for (var i = 0; i < entryFiles.length; i++) { var filePath = entryFiles[i]; var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.')); map[filename] = filePath; } return map; } //修改入口,已經(jīng)修改outp的filename module.exports = { //entry: "./src/js/index.js", entry: entries(), output: { path: path.join(__dirname, "dist"), filename: "[name].js" }, ...... //以下省略,可以見(jiàn)下文詳細(xì)配置
測(cè)試
1.在src/js目錄中新增pageA.js
//js只有兩行代碼,在body中加一句話 var $ = require("jquery") $("<div>這是jquery生成的多頁(yè)面示例</div>").appendTo("body")
2.新增pageA.html,也順便修改原來(lái)的index.html 對(duì)于js文件名的更改
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <script src="../dist/index.js"></script> </body> </html>
3.執(zhí)行webpack,啟動(dòng)dev-server
$ webpack $ webpack-dev-server
OK,成功打包生成pageA.js,成功運(yùn)行
二:公共庫(kù)單獨(dú)打包
先來(lái)分析下,上個(gè)步驟打包的日志:
index.js 依賴(lài)了avalon 和 jquery,然后打包后的index.js 有480kb
pageA.js 只用了jquery,然后打包后的js 有294kb
那么如果引用的lib庫(kù)多一點(diǎn),又被很多頁(yè)面引用,那么lib庫(kù)就會(huì)被重復(fù)打包到page.js中去,模塊越多重復(fù)加載的情況越嚴(yán)重。
如果把公共代碼提取出來(lái)作為單獨(dú)的js,那么就到處可以復(fù)用,瀏覽器也就可以進(jìn)行緩存,這時(shí)候就需要用到webpack內(nèi)置插件WebPack.optimize.CommonsChunkPlugin
CommonsChunkPlugin 介紹
使用
new webpack.optimize.CommonsChunkPlugin(options)
Options
翻譯得比較簡(jiǎn)單,詳見(jiàn)官方說(shuō)明:
- options.name or options.names(string|string[]): 公共模塊的名稱(chēng)
- options.filename (string): 公開(kāi)模塊的文件名(生成的文件名)
- options.minChunks (number|Infinity|function(module,count) - boolean): 為number表示需要被多少個(gè)entries依賴(lài)才會(huì)被打包到公共代碼庫(kù);為Infinity 僅僅創(chuàng)建公共組件塊,不會(huì)把任何modules打包進(jìn)去。并且提供function,以便于自定義邏輯。
- options.chunks(string[]):只對(duì)該chunks中的代碼進(jìn)行提取。
- options.children(boolean):如果為true,那么公共組件的所有子依賴(lài)都將被選擇進(jìn)來(lái)
- options.async(boolean|string):如果為true,將創(chuàng)建一個(gè) option.name的子chunks(options.chunks的同級(jí)chunks) 異步common chunk
- options.minSize(number):所有公共module的size 要大于number,才會(huì)創(chuàng)建common chunk
2個(gè)常用的例子,更多例子見(jiàn)官方說(shuō)明:
1.Commons chunk for entries:針對(duì)入口文件提取公共代碼
new CommonsChunkPlugin({ name: "commons", // (the commons chunk name) filename: "commons.js", // (the filename of the commons chunk) // minChunks: 3, // (Modules must be shared between 3 entries) // chunks: ["pageA", "pageB"], // (Only use these entries) })
2.Explicit vendor chunk:直接指定第三方依賴(lài)庫(kù),打包成公共組件
entry: { vendor: ["jquery", "other-lib"], app: "./entry" } new CommonsChunkPlugin({ name: "vendor", // filename: "vendor.js" // (Give the chunk a different name) minChunks: Infinity, // (with more entries, this ensures that no other module // goes into the vendor chunk) })
CommonsChunkPlugin使用
基于上篇的項(xiàng)目,參考上面的第二個(gè)例子,我們將jquery 和 avalon 提取出來(lái)打包成vendor.js
完整的webpack.config.js 如下:
```js var webpack = require("webpack"); var path = require("path"); var srcDir = path.resolve(process.cwd(), 'src'); var nodeModPath = path.resolve(__dirname, './node_modules'); var pathMap = require('./src/pathmap.json'); var glob = require('glob') var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; var entries= function () { var jsDir = path.resolve(srcDir, 'js') var entryFiles = glob.sync(jsDir + '/*.{js,jsx}') var map = {}; for (var i = 0; i < entryFiles.length; i++) { var filePath = entryFiles[i]; var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.')); map[filename] = filePath; } return map; } module.exports = { //entry: "./src/js/index.js", //entry: entries(), entry: Object.assign(entries(), { // 用到什么公共lib(例如jquery.js),就把它加進(jìn)vendor去,目的是將公用庫(kù)單獨(dú)提取打包 'vendor': ['jquery', 'avalon'] }), output: { path: path.join(__dirname, "dist"), filename: "[name].js" }, module: { loaders: [ {test: /\.css$/, loader: 'style-loader!css-loader'} ] }, resolve: { extensions: ['.js', "", ".css"], root: [srcDir,nodeModPath], alias: pathMap, publicPath: '/' }, plugins: [ new CommonsChunkPlugin({ name: 'vendor', minChunks: Infinity }) ] } ```
測(cè)試、驗(yàn)證
1.修改入口(Object.assign 是html5.js里面的....)
//entry: entries(), entry: Object.assign(entries(), { // 用到什么公共lib(例如jquery.js),就把它加進(jìn)vendor去,目的是將公用庫(kù)單獨(dú)提取打包 'vendor': ['jquery', 'avalon'] }),
2.加入插件CommonsChunkPlugin
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; config 中增加 plugins plugins: [ new CommonsChunkPlugin({ name: 'vendor', minChunks: Infinity }) ]
3.修改index.html 和 pageA.html,增加對(duì)verdor.js的引用
<script src="../dist/vendor.js"></script> <script src="../dist/index.js"></script> //<script src="../dist/pageA.js"></script>
4.執(zhí)行webpack
$ webpack
結(jié)果分析
可以看到index.js 就只有457 bytes了,pageA.js 227bytes。vendor.js 是集成了jquery+avalon,所以有488kb。
這樣vendor.js 就可以重復(fù)利用了,也方便瀏覽器進(jìn)行緩存。
調(diào)試過(guò)程中發(fā)現(xiàn)
Uncaught ReferenceError: webpackJsonp is not defined
這個(gè)是因?yàn)楫?dāng)時(shí)把vendor.js引入 放到了page.js 后面,導(dǎo)致page.js執(zhí)行異常,所以,請(qǐng)一定把vendor.js 放在前面。
生成后的index.js就很輕便了,第三方庫(kù)都被打包到vendor中了,代碼如下:
webpackJsonp([0],[ /* 0 */ /***/ function(module, exports, __webpack_require__) { /** * Created by sloong on 2016/6/1. */ //avalon 測(cè)試 var avalon = __webpack_require__(1); avalon.define({ $id: "avalonCtrl", name: "Hello Avalon!" }); /* //zepto 測(cè)試 require("zepto") $("<div>這是zepto生成的</div>").appendTo("body")*/ //jquery 測(cè)試 var $ = __webpack_require__(2) $("<div>這是jquery生成的</div>").appendTo("body") /***/ } ]);
頁(yè)面測(cè)試均正常
OK,本篇結(jié)束了。如何讓webpack 自動(dòng)在html文件中引入所需js的script標(biāo)簽,如何給js和css文件加了hash值,這樣瀏覽器每次都能檢測(cè)到文件變更,而且也不需要手動(dòng)修改引入的js文件鏈接,這些操作webpack都能輕松搞定
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
js的壓縮及jquery壓縮探討(提高頁(yè)面加載性能/保護(hù)勞動(dòng)成果)
搞定js的加密和壓縮,一方面可以提高頁(yè)面加載性能,另外一方面也希望辛苦研發(fā)出來(lái)的成果得到一定的保護(hù),感興趣的朋友可以了解下,或許對(duì)你有所幫助2013-01-01JavaScript練習(xí)小項(xiàng)目之修改div塊的顏色
這篇文章主要給大家介紹了關(guān)于JavaScript練習(xí)小項(xiàng)目之修改div塊的顏色的相關(guān)資料,文中通過(guò)舉例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-01-01原生JS實(shí)現(xiàn)的放大鏡特效示例【測(cè)試可用】
這篇文章主要介紹了原生JS實(shí)現(xiàn)的放大鏡特效,涉及javascript事件響應(yīng)及頁(yè)面元素動(dòng)態(tài)操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-12-12用file標(biāo)簽實(shí)現(xiàn)多圖文件上傳預(yù)覽
本文介紹了用file標(biāo)簽實(shí)現(xiàn)多圖文件上傳預(yù)覽的方法。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02js 實(shí)現(xiàn) input type="file" 文件上傳示例代碼
在開(kāi)發(fā)中,文件上傳必不可少但是它長(zhǎng)得又丑、瀏覽的字樣不能換,一般會(huì)讓其隱藏點(diǎn)其他的標(biāo)簽(圖片等)來(lái)時(shí)實(shí)現(xiàn)選擇文件上傳功能2013-08-08JS實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)與刷新的方法匯總
這篇文章主要給大家介紹了關(guān)于JS實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)與刷新的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用JS具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08JS實(shí)現(xiàn)動(dòng)態(tài)給標(biāo)簽控件添加事件的方法示例
這篇文章主要介紹了JS實(shí)現(xiàn)動(dòng)態(tài)給標(biāo)簽控件添加事件的方法,結(jié)合實(shí)例形式分析了javascript簡(jiǎn)單實(shí)現(xiàn)動(dòng)態(tài)添加事件的相關(guān)操作技巧,需要的朋友可以參考下2017-05-05