JS打包與代碼分割

參考來源:https://github.com/ruanyf/webpack-demos#demo01-entry-file-sourcejavascript

後面的代碼:https://github.com/947133297/lwj-webpack-demohtml

打包AMD和commonJS模塊

webpack默認支持這兩種模塊的打包java

amd.jsjquery

define('amd',function(){
    return {
       data:'content from amd module'
    }
});

commonJS.jswebpack

module.exports = {
    data:'content from commonjs module'
}

配置文件git

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
};

入口文件github

var amd = require('./js-module/amd');
var com = require('./js-module/commomJS');

console.log(amd);
console.log(com);

運行結果:可正常輸出這兩個模塊,網絡請求只有一個bundle.js,可見這兩個模塊的內容都打包進了一個js文件中web

chunk(這裏的理解偏頗)

entry chunk :包含了 webpackJsonp、__webpack_require__ 的定義 ,__webpack_require__.e用於以promise方式加載依賴後執行組件的代碼(入口文件的代碼),如:json

 

normal chunk:包含了入口文件所需的模塊的代碼,裏面調用了webpackJsonp來註冊依賴,如:promise

 

多組件代碼抽取

若是多個組件中有相同的代碼,每一個組件都都像上個例子那樣徹底打包一份,那雖然減小了網絡訪問次數,但可能致使無謂的流量消耗,由於多個模塊是能夠共用代碼的

全局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 ,語法不一樣但效果同樣就再也不舉例了

源碼分析:

  bundle一、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不會額外生成文件,而是複用以前的

  與ensure具備相同功能的是import。這二者都用於動態代碼分割,若是使用import的話須要注意promise的兼容,可使用響應的墊片庫來處理,簡單的import使用,能夠參考倉庫中的demo5

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');
});

運行結果正常。

相關文章
相關標籤/搜索