1、webpack的模塊化原理
webpack 本身維護了一套模塊系統(tǒng)魔慷,這套模塊系統(tǒng)兼容了所有前端歷史進程下的模塊規(guī)范逞敷,包括 amd commonjs es6 等
(function(modules) {
function __webpack_require__(moduleId) {
var module = {
i: moduleId,
l: false,
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
return module.exports;
}
return __webpack_require__(0);
})([(function (module, __webpack_exports__, __webpack_require__) {
// 模塊的邏輯代碼
...
})]
原理的關(guān)鍵有兩點:
1必怜、所有的模塊都被封裝成function (module, webpack_exports, webpack_require){}的形式养涮,
2、webpack_require函數(shù)罐监,該函數(shù)內(nèi)部有一個module對象吴藻,該對象有兩個重要屬性:i代表模塊id,exports代表要暴露給外面的對象弓柱。每次調(diào)用webpack_require時都會傳入一個數(shù)字作為模塊id沟堡,這樣不同模塊就能被區(qū)分,另外該函數(shù)將module.exports作為返回值吆你。每個模塊的導出值都會被記錄在module.exports對象里弦叶,其他依賴該模塊的模塊就能取到對應數(shù)據(jù)
2俊犯、webpack2是如何支持import和require兩種引入模塊的方式
首先webpack是基于node的妇多,node的模塊規(guī)范是commonjs,該規(guī)范就是使用require和module.exports來引入和導出模塊的燕侠;其次者祖,es6 module是靜態(tài)的依賴,所以可以在運行前進行代碼轉(zhuǎn)換绢彤。import的本質(zhì)其實就是require七问,webpack在進行靜態(tài)分析時會將其轉(zhuǎn)為require。這也說明了在webpack中可以混用import和require茫舶,因為二者本質(zhì)上都是require
3械巡、如何使用 webpack 的 tree-shaking 技術(shù)
webpack 的 tree-shaking 基于ES6的模塊靜態(tài)依賴機制,babel也是可以將ES6模塊轉(zhuǎn)換為commenjs模塊的饶氏,但是你一旦這樣做了就會失去tree-shaking技術(shù)讥耗。所以在使用babel轉(zhuǎn)換ES6時一般會如下配置,即只使用bable的ES6語法轉(zhuǎn)換能力疹启,不使用它的模塊處理古程,而是使用webpack2自己的模塊處理。
presets: [['babel-preset-es2015', {modules: false}]]
需要說明的是喊崖,即使在 引入模塊時使用了 es6 挣磨,但是引入的那個模塊卻是使用 commonjs 進行輸出雇逞,這也無法使用tree-shaking。
而第三方庫大多是遵循 commonjs 規(guī)范的茁裙,這也造成了引入第三方庫無法減少不必要的引入塘砸。
所以對于未來來說第三方庫要同時發(fā)布 commonjs 格式和 es6 格式的模塊。es6 模塊的入口由 package.json 的字段 module 指定晤锥。而 commonjs 則還是在 main 字段指定谣蠢。
4、import及export轉(zhuǎn)換為require和module.exports的規(guī)則是啥樣的
- es6 的 export default 都會被轉(zhuǎn)換成 exports.default
- export 的所有輸出都會添加到module.exports對象上
- 使用require去引用ES6模塊的export default輸出時查近,注意用require('**').default
- 導出
// ES6
export default 123;
export const a = 123;
const b = 3;
const c = 4;
export { b, c };
// 轉(zhuǎn)換后
exports.default = 123;
exports.a = 123;
exports.b = 3;
exports.c = 4;
exports.__esModule = true;
babel 轉(zhuǎn)換 es6 的模塊輸出邏輯非常簡單眉踱,即將所有輸出都賦值給 exports,并帶上一個標志 __esModule 表明這是個由 es6 轉(zhuǎn)換來的 commonjs 輸出霜威。
- 導入
// 1谈喳、普通導入
import a from './a.js' //引入一個 es6 模塊中的 default 輸出
// babel會這么轉(zhuǎn)
function _interopRequireDefault(obj) {
return obj && obj.__esModule
? obj
: { 'default': obj };
}
var _a = require('./a.js');
var _a2 = _interopRequireDefault(_a);
var a = _a2['default'];
// 2、通配符引入
import * as a from './a.js' //將 es6 模塊的所有命名輸出以及defalut輸出打包成一個對象賦值給a變量
// Babel內(nèi)部這么轉(zhuǎn)
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
}
else {
var newObj = {}; // (A)
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key))
newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
// 3戈泼、具名引入
import { a } from './a.js' // 將 es6 模塊的a引入
//babel這樣轉(zhuǎn)
require('./a.js').a
5婿禽、plugin && loader
- compiler & compilation 對象
compiler 對象代表了完整的 webpack 環(huán)境配置。這個對象在啟動 webpack 時被一次性建立大猛,并在所有可操作的設(shè)置中被配置扭倾,包括原始配置,loader 和插件挽绩。當在 webpack 環(huán)境中應用一個插件時膛壹,插件將收到一個編譯器對象的引用“埃可以使用它來訪問 webpack 的主環(huán)境模聋。
compilation 對象代表了一次單一的版本構(gòu)建和生成資源。當運行 webpack 開發(fā)環(huán)境中間件時唠亚,每當檢測到一個文件變化链方,一次新的編譯將被創(chuàng)建,從而生成一組新的編譯資源灶搜。一個編譯對象表現(xiàn)了當前的模塊資源祟蚀、編譯生成資源、變化的文件割卖、以及被跟蹤依賴的狀態(tài)信息前酿。編譯對象也提供了很多關(guān)鍵點回調(diào)供插件做自定義處理時選擇使用。
compiler 是 webpack 環(huán)境的代表究珊,compilation 則是 webpack 構(gòu)建內(nèi)容的代表薪者,它包含了每個構(gòu)建環(huán)節(jié)及輸出環(huán)節(jié)所對應的方法,存放著所有 module剿涮、chunk言津、asset 以及用來生成最后打包文件的 template 的信息攻人。
- 事件鉤子
事件鉤子其實就是類似 MVVM 框架的生命周期函數(shù),在特定階段能做特殊的邏輯處理悬槽。了解一些常見的事件鉤子是寫 webpack 插件的前置條件
- 插件模板
function MyPlugin(options) {}
this.opt = options
//1.函數(shù)原型上的 apply 方法會注入 compiler 對象
MyPlugin.prototype.apply = function(compiler) {
// 2.compiler 對象上掛載了相應的 webpack 事件鉤子
compiler.plugin('emit', (compilation, callback) => { // 3.事件鉤子的回調(diào)函數(shù)里能拿到編譯后的 compilation 對象
...
})
}
module.exports = MyPlugin
- Loader
1怀吻、loader 用于對模塊的源代碼進行轉(zhuǎn)換。loader 可以使你在 import 或"加載"模塊時預處理文件初婆。因此蓬坡,loader 類似于其他構(gòu)建工具中“任務(task)”,并提供了處理前端構(gòu)建步驟的強大方法磅叛。loader 可以將文件從不同的語言(如 TypeScript)轉(zhuǎn)換為 JavaScript屑咳,或?qū)?nèi)聯(lián)圖像轉(zhuǎn)換為 data URL。loader 甚至允許你直接在 JavaScript 模塊中 import CSS文件弊琴!
2兆龙、css-loader 解析 @import 和 url()路徑中指定的css內(nèi)容
3、style-loader 會把原來的 CSS 代碼插入頁面中的一個 style 標簽中
- Loader 模版
// loaderUtils 可以獲取 webpack.config.js 中的配置
var loaderUtils = require('loader-utils');
module.exports = function(source) {
console.log("start process code...");
var options = loaderUtils.getOptions(this) || {};
if(options !== {}) {
var replaceMap = options["replaceMap"];
for(var key in replaceMap) {
console.log(source);
source = source.replace(key, replaceMap[key]);
console.log(source);
}
}
return source;
};