摘要:已經(jīng)成為編譯構(gòu)建前端項(xiàng)目的必備工具。今天就來(lái)了解一下的打包機(jī)制,先從最簡(jiǎn)單的編譯打包開(kāi)始。打包后的文件先來(lái)看下打包之后的文件是什么樣,然后再反推它的打包機(jī)制。補(bǔ)充說(shuō)明本文使用的是的版本。
webpack已經(jīng)成為編譯構(gòu)建前端項(xiàng)目的必備工具。今天就來(lái)了解一下webpack的打包機(jī)制,先從最簡(jiǎn)單的js編譯打包開(kāi)始。webpack打包后的文件
先來(lái)看下webpack打包之后的文件是什么樣,然后再反推它的打包機(jī)制。補(bǔ)充說(shuō)明:本文使用的是webpack 4.x的版本。
代碼示例:
入口文件index.js,依賴(lài)a.js和b.js,而a.js和b.js都依賴(lài)c.js
代碼如下:
// index.js import {getDate} from "./a"; import {getDay} from "./b"; console.log(getDate() + " " + getDay()); // a.js import now from "./c"; export function getDate() { var date = now(); var year = date.getFullYear(); var month = date.getMonth() + 1; month = month > 9 ? month : `0${month}`; var day = date.getDate(); day = day > 9 ? day : `0${day}`; return `${year}-${month}-${day}`; } // b.js import now from "./c"; export function getDay() { var date = now(); var arr = ["日", "一", "二", "三", "四", "五", "六"]; return `周${arr[date.getDay()]}`; } // c.js export default function now() { var date = new Date(); return date; }
webpack打包后的bundle.js如下:
/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== "undefined" && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); /******/ } /******/ Object.defineProperty(exports, "__esModule", { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === "object" && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, "default", { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != "string") for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module["default"]; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, "a", getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./example/src/index.js"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./example/src/a.js": /*!**************************!* !*** ./example/src/a.js ***! **************************/ /*! exports provided: getDate */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval("__webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getDate", function() { return getDate; }); /* harmony import */ var _c__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./c */ "./example/src/c.js"); function getDate() { var date = Object(_c__WEBPACK_IMPORTED_MODULE_0__["default"])(); var year = date.getFullYear(); var month = date.getMonth() + 1; month = month > 9 ? month : `0${month}`; var day = date.getDate(); day = day > 9 ? day : `0${day}`; return `${year}-${month}-${day}`; } //# sourceURL=webpack:///./example/src/a.js?"); /***/ }), /***/ "./example/src/b.js": /*!**************************!* !*** ./example/src/b.js ***! **************************/ /*! exports provided: getDay */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval("__webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getDay", function() { return getDay; }); /* harmony import */ var _c__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./c */ "./example/src/c.js"); function getDay() { var date = Object(_c__WEBPACK_IMPORTED_MODULE_0__["default"])(); var arr = ["日", "一", "二", "三", "四", "五", "六"]; return `周${arr[date.getDay()]}`; } //# sourceURL=webpack:///./example/src/b.js?"); /***/ }), /***/ "./example/src/c.js": /*!**************************!* !*** ./example/src/c.js ***! **************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval("__webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return now; }); function now() { var date = new Date(); return date; } //# sourceURL=webpack:///./example/src/c.js?"); /***/ }), /***/ "./example/src/index.js": /*!******************************!* !*** ./example/src/index.js ***! ******************************/ /*! no exports provided */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval("__webpack_require__.r(__webpack_exports__); /* harmony import */ var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a */ "./example/src/a.js"); /* harmony import */ var _b__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./b */ "./example/src/b.js"); console.log(Object(_a__WEBPACK_IMPORTED_MODULE_0__["getDate"])() + " " + Object(_b__WEBPACK_IMPORTED_MODULE_1__["getDay"])()); //# sourceURL=webpack:///./example/src/index.js?"); /***/ }) /******/ });
簡(jiǎn)單點(diǎn),打包后的形式是這樣的:
(function(modules) { // ... })({ // ... })
其實(shí)就是自執(zhí)行函數(shù),傳入的moduels對(duì)象是下面的形式:
{ "./example/src/a.js": (function(module, __webpack_exports__, __webpack_require__) { // 模塊代碼 }), "./example/src/b.js": (function(module, __webpack_exports__, __webpack_require__) { // 模塊代碼 }), "./example/src/c.js": (function(module, __webpack_exports__, __webpack_require__) { // 模塊代碼 }), "./example/src/index.js": (function(module, __webpack_exports__, __webpack_require__) { // 模塊代碼 }) }
模塊代碼被編譯成es5格式的代碼,在執(zhí)行時(shí),自執(zhí)行函數(shù)從入口文件開(kāi)始執(zhí)行
__webpack_require__(__webpack_require__.s = "./example/src/index.js");
再來(lái)看看__webpack_require__函數(shù)的定義:
function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ }
函數(shù)返回的是module.exports,對(duì)于已經(jīng)存在于installedModules中的模塊,再次require時(shí),直接返回module.exports,而不會(huì)執(zhí)行。
根據(jù)webpack打包后的文件,可以得出打包的思路:
定義require函數(shù)
分析入口文件的依賴(lài),打包成moduels形式的代碼
生成自執(zhí)行的bundle.js,從入口文件開(kāi)始執(zhí)行
開(kāi)發(fā)簡(jiǎn)單的打包程序 將es6語(yǔ)法轉(zhuǎn)換成es5這一步通過(guò)babel來(lái)轉(zhuǎn)換
通過(guò)babylon生成AST
通過(guò)babel-core將AST重新生成源碼
/** * 獲取文件,生成AST * @param filename */ function getAst(filename) { const content = fs.readFileSync(filename, "utf-8"); return babylon.parse(content, { sourceType: "module" }); } /** * 編譯 * @param ast */ function getTranslateCode(ast) { const { code } = babel.transformFromAst(ast, null, { presets: ["env"] }); return code; }處理模塊的依賴(lài)關(guān)系
通過(guò)babel-traverse遍歷AST,找到模塊的依賴(lài)
function getDependencies(ast) { let dependencies = []; traverse(ast, { ImportDeclaration: ({node}) => { dependencies.push(node.source.value); } }); return dependencies; }解析模塊
function parse(filename, entry) { let absolutePath = path.join(entry, filename + ".js"); const ast = getAst(absolutePath); return { filename, dependence: getDependencies(ast), // 解析依賴(lài) code: getTranslateCode(ast) // 編譯成es5 }; }解析深度依賴(lài)
此時(shí)的getDependencies還只是解析一個(gè)模塊的依賴(lài),但依賴(lài)的依賴(lài)沒(méi)有解析,所以需要深度遍歷
const modules = {}; /** * 深度隊(duì)列依賴(lài) * @param main */ function getQueue(main) { if (modules[main.filename]) { return; } modules[main.filename] = main; main.dependence.forEach(dep => { let child = parse(dep, "example/src"); getQueue(child); }); }bundle函數(shù)
function bundle(queue) { let modules = ""; queue.forEach(mod => { modules += `"${mod.filename}": function(require, module, exports) {${mod.code}},` }); const result = ` (function(modules){ var installedModules = {}; function require(moduleId){ if (installedModules[moduleId]) { return installedModules[moduleId].exports; } var fn = modules[moduleId]; var module = installedModules[moduleId] = {exports: {}}; fn(require, module, module.exports); return module.exports; } require("index"); })({${modules}}) `; return result; }執(zhí)行bundle后的輸出
(function (modules) { var installedModules = {}; function require(moduleId) { if (installedModules[moduleId]) { return installedModules[moduleId].exports; } var fn = modules[moduleId]; var module = installedModules[moduleId] = {exports: {}}; fn(require, module, module.exports); return module.exports; } require("index"); })({ "index": function (require, module, exports) { "use strict"; var _a = require("./a"); var _b = require("./b"); console.log((0, _a.getDate)() + " " + (0, _b.getDay)()); }, "./a": function (require, module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDate = getDate; var _c = require("./c"); var _c2 = _interopRequireDefault(_c); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : {default: obj}; } function getDate() { var date = (0, _c2.default)(); var year = date.getFullYear(); var month = date.getMonth() + 1; month = month > 9 ? month : "0" + month; var day = date.getDate(); day = day > 9 ? day : "0" + day; return year + "-" + month + "-" + day; } }, "./c": function (require, module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = now; function now() { var date = new Date(); return date; } }, "./b": function (require, module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDay = getDay; var _c = require("./c"); var _c2 = _interopRequireDefault(_c); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : {default: obj}; } function getDay() { var date = (0, _c2.default)(); var arr = ["日", "一", "二", "三", "四", "五", "六"]; return "u5468" + arr[date.getDay()]; } } })總結(jié)
這樣,基本上處理js依賴(lài)和編譯的工作算是完成了
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/95469.html
摘要:首先聲明一下,和兩者關(guān)系不大,主要是團(tuán)隊(duì)之前一直用構(gòu)建工具,這幾天業(yè)務(wù)上比較清閑,老大讓我學(xué)學(xué)新的和這些潮流工具,于是草草研究了一天,記一些筆記。最后使用將各個(gè)組件打包在一起。 首先聲明一下,gulp和webpack兩者關(guān)系不大,主要是團(tuán)隊(duì)之前一直用grunt構(gòu)建工具,這幾天業(yè)務(wù)上比較清閑,老大讓我學(xué)學(xué)新的gulp和webpack這些潮流工具,于是草草研究了一天,記一些筆記。 gulp...
摘要:我們還想要的顏色我們通過(guò)下面命令行可以做到觀(guān)察模式我們不想每一次項(xiàng)目變化都要手動(dòng)編譯可以緩存兩次編譯之間沒(méi)有變化的模塊和輸出文件。開(kāi)發(fā)服務(wù)器使用開(kāi)發(fā)服務(wù)器開(kāi)發(fā)體驗(yàn)會(huì)更好這將在本地啟動(dòng)一個(gè)端口的服務(wù),指向靜態(tài)文件以及自動(dòng)編譯。 原文英文版來(lái)自webpack官網(wǎng) demo代碼 本文地址 歡迎瀏覽 這是一篇通過(guò)一個(gè)小例子給你介紹webpack的文章 你可以通過(guò)這篇文章了解到: 如...
摘要:我們還想要的顏色我們通過(guò)下面命令行可以做到觀(guān)察模式我們不想每一次項(xiàng)目變化都要手動(dòng)編譯可以緩存兩次編譯之間沒(méi)有變化的模塊和輸出文件。開(kāi)發(fā)服務(wù)器使用開(kāi)發(fā)服務(wù)器開(kāi)發(fā)體驗(yàn)會(huì)更好這將在本地啟動(dòng)一個(gè)端口的服務(wù),指向靜態(tài)文件以及自動(dòng)編譯。 原文英文版來(lái)自webpack官網(wǎng) demo代碼 本文地址 歡迎瀏覽 這是一篇通過(guò)一個(gè)小例子給你介紹webpack的文章 你可以通過(guò)這篇文章了解到: 如...
摘要:我們還想要的顏色我們通過(guò)下面命令行可以做到觀(guān)察模式我們不想每一次項(xiàng)目變化都要手動(dòng)編譯可以緩存兩次編譯之間沒(méi)有變化的模塊和輸出文件。開(kāi)發(fā)服務(wù)器使用開(kāi)發(fā)服務(wù)器開(kāi)發(fā)體驗(yàn)會(huì)更好這將在本地啟動(dòng)一個(gè)端口的服務(wù),指向靜態(tài)文件以及自動(dòng)編譯。 原文英文版來(lái)自webpack官網(wǎng) demo代碼 本文地址 歡迎瀏覽 這是一篇通過(guò)一個(gè)小例子給你介紹webpack的文章 你可以通過(guò)這篇文章了解到: 如...
閱讀 2397·2021-10-09 09:44
閱讀 2132·2021-10-08 10:05
閱讀 3424·2021-07-26 23:38
閱讀 2991·2019-08-28 18:16
閱讀 812·2019-08-26 11:55
閱讀 1821·2019-08-23 18:29
閱讀 2035·2019-08-23 18:05
閱讀 1364·2019-08-23 17:02