打包js
參考來源:https://github.com/ruanyf/webpack-demos#demo01-entry-file-source
後面的代碼:https://github.com/947133297/lwj-webpack-demo
打包AMD和commonJS模塊
webpack默認支持這兩種模塊的打包
amd.js
define(‘amd‘,function(){ return { data:‘content from amd module‘ } });
commonJS.js
module.exports = { data:‘content from commonjs module‘ }
配置文件
module.exports = { entry: ‘./main.js‘, output: { filename: ‘bundle.js‘ } };
入口文件
var amd = require(‘./js-module/amd‘); var com = require(‘./js-module/commomJS‘); console.log(amd); console.log(com);
運行結果:可正常輸出這兩個模塊,網絡請求只有一個bundle.js,可見這兩個模塊的內容都打包進了一個js文件中
chunk(這裏的理解偏頗)
entry chunk :包含了 webpackJsonp、__webpack_require__ 的定義 ,__webpack_require__.e用於以promise方式加載依賴後執行組件的代碼(入口文件的代碼),如:
normal chunk:包含了入口文件所需的模塊的代碼,裏面調用了webpackJsonp來註冊依賴,如:
多組件代碼抽取
如果多個組件中有相同的代碼,每個組件都都像上個例子那樣完全打包一份,那雖然減少了網絡訪問次數,但可能導致無謂的流量消耗,因為多個模塊是可以共用代碼的
全局JS:
把要復用的部分抽取到一個額外的全局js中,然後手動引入這個js,其他組件中直接使用這個js即可,如:
module.exports = { entry: { bundle1:‘./main1.js‘, bundle2:‘./main2.js‘ }, output: { filename:‘[name].js‘ } }; // main1.js console.log(‘組件1獲取數據‘ + data); //main2.js console.log(‘組件2獲取數據‘ + data); //commonData.js: var data = ‘this is common data‘; index.html: <html> <script src="commonData.js"></script> <body> <script type="text/javascript" src="bundle1.js"></script> <script type="text/javascript" src="bundle2.js"></script> </body> </html>
有沒有辦法不全局,而且可以使多個組件共用呢?這個需求可唯一推測出只能使用js模塊,但是以上測試中,發現使用模塊的話,會完全打包進去,以至於不能實現代碼復用。其他webpack提供一個require.ensure可以實現模塊的不打包,從而達到復用的目的
require.ensure
對以上代碼進行改造:
// commonData.js exports.data = ‘this is common data‘; // mian1.js require.ensure([‘./commonData‘], function(require) { var m = require(‘./commonData‘); console.log(‘組件1獲取數據‘ + m.data); }); //main2.js require.ensure([‘./commonData‘], function(require) { var m = require(‘./commonData‘); console.log(‘組件2獲取數據‘ + m.data); }); //index.html <html> <body> <script type="text/javascript" src="bundle1.js"></script> <script type="text/javascript" src="bundle2.js"></script> </body> </html>
運行結果一樣。只不過這次實現了代碼復用:
以上效果的另外一種實現方式,是使用 bundle-loader ,語法不同但效果一樣就不再舉例了
源碼分析:
bundle1、2.js中包含了webpackJsonp、__webpack_require__以及這個函數對象的一系列方法,以及對應入口文件的執行代碼
__webpack_require__.e 函數:用於根據模塊id來獲取模塊,返回一個promise。往header上寫一個script標簽,這個標簽onload的時候,也就是這個新增script中的webpackJsonp函數執行完了,就觸發promise的resolve方法(執行了對應入口文件的代碼)
補充測試:
模塊1和2ensure了一個文件,模塊3ensure了另一個文件。則會額外生成0.js和1.js。也就是說多少個文件被ensure,就會額外生成多少個文件(名字從0開始計算),重復ensure不會額外生成文件,而是復用之前的
CommonsChunkPlugin
代碼抽取還可以用這個插件來實現,測試代碼如下:
抽取依賴
// 配置文件 var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin"); module.exports = { entry: { bundle1: ‘./main1.js‘, bundle2: ‘./main2.js‘ }, output: { filename: ‘[name].js‘ }, plugins: [ new CommonsChunkPlugin(‘chunkInit‘) ] } // main1.js require(‘./com‘); require(‘./comm2‘); var data = ‘this is differenet1 ‘; //main2.js require(‘./com‘); require(‘./comm2‘); var data = ‘this is differenet2 ‘; //com.js exports.data = ‘這裏存放的是公共代碼‘ //comm2.js exports.data = ‘這裏存放的是公共代碼2‘ //index.html <html> <body> <script src="chunkInit.js"></script> <script src="bundle1.js"></script> <script src="bundle2.js"></script> </body> </html>
運行結果:
會生成一個chunkinit.js文件(這個名字是我們在new 插件的時候指定的),這個文件中包含了webpackJsonp、__webpack_require__以及這個函數對象的一系列方法以及被依賴的模塊的代碼,也就是說require進來的東西並不是動態加載的,而且直接寫到了這個chunkinit.js文件中。而兩個bundle文件調用了webpackjsonP函數,裏面的回調函數中執行了對應入口文件的代碼
可見,這個插件實現的功能和require.ensure都是代碼抽取,但兩者本質上不同:
組件的初始化定義:包含了webpackJsonp、__webpack_require__以及這個函數對象的一系列方法
ensure:動態加載代碼,抽取的是被依賴的代碼,多個組件的初始化定義也還是重復的
插件:非動態加載代碼,抽取的是多個組件的初始化定義到一個文件中,並且裏面寫好了被依賴的模塊代碼
以上兩個組件都引用了兩個相同的依賴,所以這兩個依賴都放到了chuninit中。測試發現,多個組件中相同的依賴會被放到chunkinti中,而差異的部分則放到對應的bundle中,也就是說bundle中可能同時出現出現被依賴的模塊代碼以及對應的入口文件的代碼
使用jquery
回顧以下:jq這個庫兼容amd和commonJS的導入方式,require.ensure可以以不打包的形式導入一個庫
有了以上的理解,怎麽使用jq就很好想象了,以下使用ensure來導入,同樣的理解使用上面的插件來導入應該也是可行的
require.ensure([‘./jquery-3.2.1.min‘], function(require) { var jq = require(‘./jquery-3.2.1.min‘); jq(‘body‘).attr(‘c2‘,‘true‘); });
運行結果正常。
打包js