摘要:已经成为编译构建前端项目的必备工具。今天就来了解一下的打包机制,先从最简单的编译打包开始。打包后的文件先来看下打包之后的文件是什么样,然后再反推它的打包机制。补充说明本文使用的是的版本。
webpack已经成为编译构建前端项目的必备工具。今天就来了解一下webpack的打包机制,先从最简单的js编译打包开始。webpack打包后的文件
先来看下webpack打包之后的文件是什么样,然后再反推它的打包机制。补充说明:本文使用的是webpack 4.x的版本。
代码示例:
入口文件index.js,依赖a.js和b.js,而a.js和b.js都依赖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?"); /***/ }) /******/ });
简单点,打包后的形式是这样的:
(function(modules) { // ... })({ // ... })
其实就是自执行函数,传入的moduels对象是下面的形式:
{ "./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格式的代码,在执行时,自执行函数从入口文件开始执行
__webpack_require__(__webpack_require__.s = "./example/src/index.js");
再来看看__webpack_require__函数的定义:
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; /******/ }
函数返回的是module.exports,对于已经存在于installedModules中的模块,再次require时,直接返回module.exports,而不会执行。
根据webpack打包后的文件,可以得出打包的思路:
定义require函数
分析入口文件的依赖,打包成moduels形式的代码
生成自执行的bundle.js,从入口文件开始执行
开发简单的打包程序 将es6语法转换成es5这一步通过babel来转换
通过babylon生成AST
通过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; }处理模块的依赖关系
通过babel-traverse遍历AST,找到模块的依赖
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), // 解析依赖 code: getTranslateCode(ast) // 编译成es5 }; }解析深度依赖
此时的getDependencies还只是解析一个模块的依赖,但依赖的依赖没有解析,所以需要深度遍历
const modules = {}; /** * 深度队列依赖 * @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函数
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; }执行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()]; } } })总结
这样,基本上处理js依赖和编译的工作算是完成了
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/95469.html
摘要:首先声明一下,和两者关系不大,主要是团队之前一直用构建工具,这几天业务上比较清闲,老大让我学学新的和这些潮流工具,于是草草研究了一天,记一些笔记。最后使用将各个组件打包在一起。 首先声明一下,gulp和webpack两者关系不大,主要是团队之前一直用grunt构建工具,这几天业务上比较清闲,老大让我学学新的gulp和webpack这些潮流工具,于是草草研究了一天,记一些笔记。 gulp...
阅读 2369·2021-10-09 09:44
阅读 2061·2021-10-08 10:05
阅读 3407·2021-07-26 23:38
阅读 2929·2019-08-28 18:16
阅读 761·2019-08-26 11:55
阅读 1781·2019-08-23 18:29
阅读 1995·2019-08-23 18:05
阅读 1331·2019-08-23 17:02