浅谈webpack编译vue项目生成的代码探索

前端之家收集整理的这篇文章主要介绍了浅谈webpack编译vue项目生成的代码探索前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

本文介绍了webpack编译vue项目生成代码探索,分享给大家,具体如下:

前言

往 main.js 里写入最简单的 vue 项目结构如下

new Vue({
el: '#app',template: '',components: {
App
}
})

App.vue 如下

编译生成后得到一个316kb的文件,而在316Kb中包含着什么,我很好奇想探索一番。

learning-in-vue@1.0.0 build /Users/everlose/workspace/github/learningInVue
cross-env NODE_ENV=production webpack --progress --hide-modules

Hash: 18d868a423b48dc263e9
Version: webpack 3.9.1
Time: 3693ms
Asset Size Chunks Chunk Names
build.js 316 kB 0 [emitted] [big] main
build.js.map 399 kB 0 [emitted] main

代码分解

按顺序往下解读,本篇编译后的代码在这儿,如果只想看结论那么请拉到最后有一张结构梳理图。

webpack 模块机制

前面70行还是熟悉的 webpack 模块机制的基础代码,关于它的细致解读参见上一篇webpack模块机制,编译后的代码格式如下,并且我做了代码美化,并且插上了中文注释

属性,靠函数__webpack_require__做到。 /******/ 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) /******/ // 创造一个新模块并放入缓存中,i是模块标识,l意为是否加载此模块完毕,exports是此模块执行后的输出对象 /******/ 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 标为true代表模块执行完成。 /******/ module.l = true; /******/ /******/ // Return the exports of the module 返回此模块输出的对象 /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ // webpack 私有变量,保存传入的modules,即所有的模块组成的数组 /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ // 保存缓存中的模块数组 /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ // 为 es6 exports 定义 getter /******/ __webpack_require__.d = function(exports,name,getter) { /******/ // 如果 exports 输出的对象本身不包含 name 属性时,定义一个。 /******/ if(!__webpack_require__.o(exports,name)) { /******/ Object.defineProperty(exports,{ /******/ configurable: false,/******/ enumerable: true,/******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ // 解决 ES module 和 Common js module 的冲突,ES 则返回 module['default'] /******/ __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 /******/ // 工具方法,判断是否object有property属性。 /******/ __webpack_require__.o = function(object,property) { return Object.prototype.hasOwnProperty.call(object,property); }; /******/ /******/ // __webpack_public_path__ /******/ // 大概和 webpack.config.js 的 output 有关吧,webpack 的公共路径 /******/ __webpack_require__.p = "/dist/"; /******/ /******/ // Load entry module and return exports 执行第一个依赖模块并且返回它输出。 /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ })

0号模块

导出一个全局变量,在web端就是指代window

var g;

// This works in non-strict mode
g = (function () {
return this;
})();

try {
// This works if eval is allowed (see CSP)
g = g || Function("return this")() || (1,eval)("this");
} catch (e) {
// This works if the window reference is available
if (typeof window === "object")
g = window;
}

// g can still be undefined,but nothing to do about it...
// We return undefined,instead of nothing here,so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;

/***/
}),

1号模块

实际上做的事情很明显,就是导出了 main.js 的代码,一个vue实例对象

"use strict";
Object.defineProperty(webpack_exports,"esModule",{ value: true });
/ harmony import / var WEBPACK_IMPORTED_MODULE_0_vue = __webpack_require
(2);
/ harmony import / var WEBPACK_IMPORTED_MODULE_1__App_vue = __webpack_require__(6);

// 从2号模块导出的一个叫a的变量,就是Vue对象本身
new WEBPACK_IMPORTED_MODULE_0_vue["a" / default /]({
el: '#app',components: {
App: WEBPACK_IMPORTED_MODULE_1__App_vue["a" / default /]
}
});

/***/ })

2号模块

即是 Vue 源码本身,从114行一直到了10818行,一共是10705行代码,啧啧啧

webpack 有所配置,所以导出的 Vue 实际上是 vue/dist/vue.esm.js 的完整编译版本。

"use strict";
/*!

  • Vue.js v2.5.9
  • (c) 2014-2017 Evan You
  • Released under the MIT License.
    */

// 作用域指向webpack_exports,并把webpack_require(0)作为global,实际上就是window
// __webpack_require__(3).setImmediate)作为setsetImmediate参数传入函数
(function (global,setImmediate) {

// 省略近1w行的代码,关于vue原本本身的解读以后再做......

// 最终 export 出来一个叫 Vue$3的对象
/ harmony default export /
__webpack_exports__["a"] = (Vue$3);

/ WEBPACK VAR INJECTION /
}.call(webpack_exports,webpack_require(0),__webpack_require__(3).setImmediate))

}),

3,4,5号模块

都和 node_modules/setimmediate 有关,由于 vue 的 DOM 异步更新机制使用到了它,所以被引入。

这里也不做详解,只给出结构。

// 省略代码...

// setimmediate attaches itself to the global object
__webpack_require__(4);
exports.setImmediate = setImmediate;
exports.clearImmediate = clearImmediate;

//
}),/
4 /
/
/
(function (module,__webpack_require__) {

/ WEBPACK VAR INJECTION /
(function (global,process) {
// 省略代码...
}.call(exports,__webpack_require__(5)))

//
}),/
5 /
/
/
(function (module,exports) {

// shim for using process in browser
var process = module.exports = {};

// 省略代码...

process.cwd = function () {
return '/'
};
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function () {
return 0;
};
/***/
}),

6号模块

和 App.vue 的解析有关,把 App.vue 中的 template 和 script 编译为一个 vue components,并把 style 标签内的样式插入到DOM中。

"use strict";

// 返回具体 App.vue 中 的script 中的代码
var WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_App_vue = __webpack_require__(13);

// 把App.vue 的 template 解析为一堆 vue render 函数
var WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_66ce2159_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_App_vue = __webpack_require__(14);

// 注入vue文件里写入的css函数
function injectStyle(ssrContext) {
// 由此可知7号模块是编译并插入vue中的css到DOM上的
webpack_require(7)
}
// 12号模块用于输出components渲染函数
var normalizeComponent =
webpack_require
(12)
/ script /

/ template /

/ template functional /
var vue_template_functional = false
/ styles /
var vue_styles = injectStyle
/ scopeId /
var
vue_scopeId
= null
/ moduleIdentifier (server only) /
var vue_module_identifier = null

// 编译模块,混杂template和script。
var Component = normalizeComponent(
WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_App_vue["a" / default / ],WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_66ce2159_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_App_vue["a" / default / ],vue_template_functional,vue_styles,vue_scopeId,vue_module_identifier
)

/ harmony default export /
__webpack_exports__["a"] = (Component.exports);

/***/
}),

7、8、9、10、11

都和样式有关,简言之就是7号模块加载8号模块获取css代码作为参数,并作为参数传入10号模块进行插入

太长也只大意上列出结构

  1. 7号模块由 style-loader 带入,把所有的css插入到 style 标签
  2. 8号模块加载具体的css代码
  3. 9号模块由css-loader代入,用于做css的sourceMap
  4. 10号模块返回具体插入动作函数,供7号模块调用
  5. 11号模块把所有的样式组成的数组转为字符串,给10号模块做插入。
/***/
(function (module,__webpack_require__) {

// style-loader: Adds some css to the DOM by adding a