以前介紹過webpack3的新特性,裏面提到webpack2支持了ES6的import和export,不須要將ES6的模塊先轉成CommonJS模塊,而後再進行打包處理。正基於此,webpack2引入了tree-shaking技術,可以在模塊的層面上作到打包後的代碼只包含被引用並被執行的模塊,而不被引用或不被執行的模塊被刪除掉,以起到減包的效果。javascript
下面結合實際代碼來解釋webpack2是如何實現tree-shaking的,示例代碼可到github進行下載。css
示例代碼結構如圖:src中index.js爲入口文件,module.js是測試的模塊文件,dist中是產出的文件。java
根據webpack官網的提示,webpack支持tree-shaking,須要修改配置文件,指定babel處理js文件時不要將ES6模塊轉成CommonJS模塊,具體作法就是:node
在.babelrc設置babel-preset-es2015的modules爲fasle,表示不對ES6模塊進行處理。webpack
// .babelrc文件 { "presets": [ ["es2015", { "modules": false }] ], "comments": false }
在webpack.config.js中設置babel-preset-es2015的modules爲fasle,表示不對ES6模塊進行處理。 git
// webpack.config.js ... rules: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ ["es2015", { "modules": false }] ] } } ] ...
而後在module.js文件中建立三個模塊sayHello,sayBye,sayHi,並在index.js引用sayHello,sayHi;github
// module.js export const sayHello = name => `Hello ${name}!`; export const sayBye = name => `Bye ${name}!`; export const sayHi = name => `Hi ${name}!`;
// index.js import { sayHello } from './module'; import { sayHi } from './module'; const element = document.createElement('h1'); element.innerHTML = sayHello('World') + sayHi('my friend'); document.body.appendChild(element);
而後在當前目錄執行 webpack 命令後,產出bundle.js的代碼以下web
/* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return sayHello; }); /* unused harmony export sayBye */ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return sayHi; }); var sayHello = function sayHello(name) { return "Hello " + name + "!"; }; var sayBye = function sayBye(name) { return "Bye " + name + "!"; }; var sayHi = function sayHi(name) { return "Hi " + name + "!"; }; /***/ }), /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__module__ = __webpack_require__(0); var element = document.createElement('h1'); element.innerHTML = Object(__WEBPACK_IMPORTED_MODULE_0__module__["a" /* sayHello */])('World') + Object(__WEBPACK_IMPORTED_MODULE_0__module__["b" /* sayHi */])(' to meet you'); document.body.appendChild(element); /***/ })
從上面能夠知道,sayBye模塊被打上了unused harmony export標籤,sayHello和sayHi被設置爲__webpack_exports__的屬性,在入口文件中經過讀取__webpack_exports__的屬性取出。babel
bundle.js文件雖然對多餘的模塊進行了標記,可是並無刪除,這是由於webpack尚未執行壓縮混淆操做,能夠經過webpack -p命令對產出進行壓縮處理,這時候會把打了unused harmony export 標籤的模塊刪除掉。app
(1)只能是靜態聲明和引用的ES6模塊,不能是動態引入和聲明的;
在打包階段對冗餘代碼進行刪除,就須要webpack須要在打包階段肯定模塊文件的內部結構,而ES模塊的引用和輸出必須出如今文件結構的第一級('import' and 'export' may only appear at the top level),不然會報錯。
// webpack編譯時會報錯 if (condition) { import module1 from './module1'; } else { import module2 from './module2'; }
而CommonJS模塊支持動態結構的,因此不能對CommonJS模塊進行tree-shaking處理。
(2)只能處理模塊級別,不能處理函數級別的冗餘;
由於webpack的tree-shaking是基於模塊間的依賴關係,因此並不能對模塊內部自身的無用代碼進行刪除。
(3)只能處理JS相關冗餘代碼,不能處理CSS冗餘代碼。
目前webpack只對JS文件的依賴進行了處理,CSS的冗餘並無給出很好的工具。最近聽了一個講座,提到了webpack-css-treeshaking-plugin,該插件基於AST對CSS冗餘代碼進行了很好的處理。