淺談webpack編譯vue項目生成的代碼探索
本文介紹了webpack編譯vue項目生成的代碼探索,分享給大家,具體如下:
前言
往 main.js 里寫入最簡單的 vue 項目結(jié)構(gòu)如下
import Vue from 'vue';
import App from './App.vue';
new Vue({
el: '#app',
template: '<App/>',
components: {
App
}
})
App.vue 如下
<template>
<div id="app">
<h1>{{ msg }}</h1>
<h2>Essential Links</h2>
<ul>
<li>
<a rel="external nofollow" target="_blank">Core Docs</a>
</li>
<li>
<a rel="external nofollow" target="_blank">Forum</a>
</li>
<li>
<a rel="external nofollow" target="_blank">Community Chat</a>
</li>
<li>
<a rel="external nofollow" target="_blank">Twitter</a>
</li>
</ul>
<h2>Ecosystem</h2>
<ul>
<li>
<a rel="external nofollow" target="_blank">vue-router</a>
</li>
<li>
<a rel="external nofollow" target="_blank">vuex</a>
</li>
<li>
<a rel="external nofollow" target="_blank">vue-loader</a>
</li>
<li>
<a rel="external nofollow" target="_blank">awesome-vue</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
h1,
h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
編譯生成后得到一個316kb的文件,而在316Kb中包含著什么,我很好奇想探索一番。
npm run build
> 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
代碼分解
按順序往下解讀,本篇編譯后的代碼在這兒,如果只想看結(jié)論那么請拉到最后有一張結(jié)構(gòu)梳理圖。
webpack 模塊機制
前面70行還是熟悉的 webpack 模塊機制的基礎代碼,關于它的細致解讀參見上一篇webpack模塊機制,編譯后的代碼格式如下,并且我做了代碼美化,并且插上了中文注釋
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ // 緩存模塊,所有被加載過的模塊都會成為installedModules對象的屬性,靠函數(shù)__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)
/******/ // 創(chuàng)造一個新模塊并放入緩存中,i是模塊標識,l意為是否加載此模塊完畢,exports是此模塊執(zhí)行后的輸出對象
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ // 傳入?yún)?shù)并執(zhí)行模塊函數(shù)
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded 標為true代表模塊執(zhí)行完成。
/******/ module.l = true;
/******/
/******/ // Return the exports of the module 返回此模塊輸出的對象
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ // webpack 私有變量,保存?zhèn)魅氲膍odules,即所有的模塊組成的數(shù)組
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ // 保存緩存中的模塊數(shù)組
/******/ __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, name, {
/******/ 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 執(zhí)行第一個依賴模塊并且返回它輸出。
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
0號模塊
導出一個全局變量,在web端就是指代window
/* 0 */
(function (module, exports) {
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實例對象
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"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',
template: '<App/>',
components: {
App: __WEBPACK_IMPORTED_MODULE_1__App_vue__["a" /* default */]
}
});
/***/ })
2號模塊
即是 Vue 源碼本身,從114行一直到了10818行,一共是10705行代碼,嘖嘖嘖
webpack 有所配置,所以導出的 Vue 實際上是 vue/dist/vue.esm.js 的完整編譯版本。
/* 2 */
/***/ (function (module, __webpack_exports__, __webpack_require__) {
"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參數(shù)傳入函數(shù)
(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 異步更新機制使用到了它,所以被引入。
這里也不做詳解,只給出結(jié)構(gòu)。
/* 3 */
/***/
(function (module, exports, __webpack_require__) {
// 省略代碼...
// setimmediate attaches itself to the global object
__webpack_require__(4);
exports.setImmediate = setImmediate;
exports.clearImmediate = clearImmediate;
/***/
}),
/* 4 */
/***/
(function (module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */
(function (global, process) {
// 省略代碼...
}.call(exports, __webpack_require__(0), __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 標簽內(nèi)的樣式插入到DOM中。
/* 6 */
/***/
(function (module, __webpack_exports__, __webpack_require__) {
"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 函數(shù)。
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函數(shù)
function injectStyle(ssrContext) {
// 由此可知7號模塊是編譯并插入vue中的css到DOM上的
__webpack_require__(7)
}
// 12號模塊用于輸出components渲染函數(shù)
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代碼作為參數(shù),并作為參數(shù)傳入10號模塊進行插入
太長也只大意上列出結(jié)構(gòu)
- 7號模塊由 style-loader 帶入,把所有的css插入到 style 標簽里
- 8號模塊加載具體的css代碼,
- 9號模塊由css-loader代入,用于做css的sourceMap
- 10號模塊返回具體插入動作函數(shù),供7號模塊調(diào)用
- 11號模塊把所有的樣式組成的數(shù)組轉(zhuǎn)為字符串,給10號模塊做插入。
/* 7 */
/***/
(function (module, exports, __webpack_require__) {
// style-loader: Adds some css to the DOM by adding a <style> tag
// load the styles
var content = __webpack_require__(8);
if (typeof content === 'string') content = [
[module.i, content, '']
];
if (content.locals) module.exports = content.locals;
// add the styles to the DOM
var update = __webpack_require__(10)("15459d21", content, true);
/***/
}),
/* 8 */
/***/
(function (module, exports, __webpack_require__) {
// css-loader 用于做css的sourceMap
exports = module.exports = __webpack_require__(9)(undefined);
// imports
// module
// 這就是 App.vue 文件中 style 里的的 css
exports.push([module.i, "#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50;margin-top:60px}h1,h2{font-weight:400}ul{list-style-type:none;padding:0}li{display:inline-block;margin:0 10px}a{color:#42b983}", ""]);
// exports
/***/
}),
/* 9 */
/***/
(function (module, exports) {
// css base code, injected by the css-loader
module.exports = function (useSourceMap) {
// 省略代碼...
}
}),
/* 10 */
/***/
(function (module, exports, __webpack_require__) {
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
Modified by Evan You @yyx990803
*/
// ...太長只貼了關鍵步驟,總之關鍵的函數(shù)就是這些
var hasDocument = typeof document !== 'undefined'
// ...
var listToStyles = __webpack_require__(11)
// ...
var head = hasDocument && (document.head || document.getElementsByTagName('head')[0])
// ...
module.exports = function (parentId, list, _isProduction) {
// ...
var styles = listToStyles(parentId, list)
addStylesToDom(styles)
return function update (newList) {
// ...
}
}
function addStylesToDom (styles /* Array<StyleObject> */) {
for (var i = 0; i < styles.length; i++) {
// ...
domStyle.parts.push(addStyle(item.parts[j]))
// ....
}
}
// 總之先調(diào)用了addStylesToDom,接著是addStyle,再是createStyleElement插入樣式到head中。
function createStyleElement () {
var styleElement = document.createElement('style')
styleElement.type = 'text/css'
head.appendChild(styleElement)
return styleElement
}
function addStyle (obj /* StyleObjectPart */) {
// ...
styleElement = createStyleElement()
// ...
}
/***/
}),
/* 11 */
/***/
(function (module, exports) {
/**
* Translates the list format produced by css-loader into something
* easier to manipulate.
*/
module.exports = function listToStyles(parentId, list) {
var styles = []
var newStyles = {}
for (var i = 0; i < list.length; i++) {
var item = list[i]
var id = item[0]
var css = item[1]
var media = item[2]
var sourceMap = item[3]
var part = {
id: parentId + ':' + i,
css: css,
media: media,
sourceMap: sourceMap
}
if (!newStyles[id]) {
styles.push(newStyles[id] = {
id: id,
parts: [part]
})
} else {
newStyles[id].parts.push(part)
}
}
return styles
}
/***/
}),
12、13、14號模塊
12號做 .vue 文件中的 template 和 script 解析并供6號輸出,最終返回了一個 vue components 對象,在瀏覽器控制臺打印如下:
Object
beforeCreate: [ƒ]
data: ƒ data()
inject: {}
name: "app"
render: ƒ ()
staticRenderFns: (2) [ƒ, ƒ, cached: Array(2)]
_Ctor: {0: ƒ}
_compiled: true
__proto__: Object
而13號模塊返回具體 script 中的代碼,而14號模塊則是把 template 解析為一堆 vue render 函數(shù)。
/* 12 */
/***/
(function (module, exports) {
/* globals __VUE_SSR_CONTEXT__ */
// IMPORTANT: Do NOT use ES2015 features in this file.
// This module is a runtime utility for cleaner component module output and will
// be included in the final webpack user bundle.
module.exports = function normalizeComponent(
rawScriptExports,
compiledTemplate,
functionalTemplate,
injectStyles,
scopeId,
moduleIdentifier /* server only */
) {
var esModule
var scriptExports = rawScriptExports = rawScriptExports || {}
// ES6 modules interop
var type = typeof rawScriptExports.default
if (type === 'object' || type === 'function') {
esModule = rawScriptExports
scriptExports = rawScriptExports.default
}
// Vue.extend constructor export interop
var options = typeof scriptExports === 'function' ?
scriptExports.options :
scriptExports
// render functions
if (compiledTemplate) {
options.render = compiledTemplate.render
options.staticRenderFns = compiledTemplate.staticRenderFns
options._compiled = true
}
// functional template
if (functionalTemplate) {
options.functional = true
}
// scopedId
if (scopeId) {
options._scopeId = scopeId
}
var hook
if (moduleIdentifier) { // server build
hook = function (context) {
// 2.3 injection
context =
context || // cached call
(this.$vnode && this.$vnode.ssrContext) || // stateful
(this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__
}
// inject component styles
if (injectStyles) {
injectStyles.call(this, context)
}
// register component module identifier for async chunk inferrence
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier)
}
}
// used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook
} else if (injectStyles) {
hook = injectStyles
}
if (hook) {
var functional = options.functional
var existing = functional ?
options.render :
options.beforeCreate
if (!functional) {
// inject component registration as beforeCreate hook
options.beforeCreate = existing ?
[].concat(existing, hook) :
[hook]
} else {
// for template-only hot-reload because in that case the render fn doesn't
// go through the normalizer
options._injectStyles = hook
// register for functioal component in vue file
options.render = function renderWithStyleInjection(h, context) {
hook.call(context)
return existing(h, context)
}
}
}
return {
esModule: esModule,
exports: scriptExports,
options: options
}
}
/***/
}),
/* 13 */
/***/
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony default export */
// 這就是 App.vue 中 script 的部分
__webpack_exports__["a"] = ({
name: 'app',
data: function data() {
return {
msg: 'Welcome to Your Vue.js App'
};
}
});
/***/
}),
/* 14 */
/***/
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
// 把template 解析為一堆 render 函數(shù),扔給vue處理最終編譯成Vnode節(jié)點在渲染成DOM輸出到視圖
var render = function () {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c('div', {
attrs: {
"id": "app"
}
}, [_c('h1', [_vm._v(_vm._s(_vm.msg))]), _vm._v(" "), _c('h2', [_vm._v("Essential Links")]), _vm._v(" "), _vm._m(0, false, false), _vm._v(" "), _c('h2', [_vm._v("Ecosystem")]), _vm._v(" "), _vm._m(1, false, false)])
}
var staticRenderFns = [function () {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c('ul', [_c('li', [_c('a', {
attrs: {
"href": "https://vuejs.org",
"target": "_blank"
}
}, [_vm._v("Core Docs")])]), _vm._v(" "), _c('li', [_c('a', {
attrs: {
"href": "https://forum.vuejs.org",
"target": "_blank"
}
}, [_vm._v("Forum")])]), _vm._v(" "), _c('li', [_c('a', {
attrs: {
"href": "https://chat.vuejs.org",
"target": "_blank"
}
}, [_vm._v("Community Chat")])]), _vm._v(" "), _c('li', [_c('a', {
attrs: {
"href": "https://twitter.com/vuejs",
"target": "_blank"
}
}, [_vm._v("Twitter")])])])
}, function () {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c('ul', [_c('li', [_c('a', {
attrs: {
"href": "http://router.vuejs.org/",
"target": "_blank"
}
}, [_vm._v("vue-router")])]), _vm._v(" "), _c('li', [_c('a', {
attrs: {
"href": "http://vuex.vuejs.org/",
"target": "_blank"
}
}, [_vm._v("vuex")])]), _vm._v(" "), _c('li', [_c('a', {
attrs: {
"href": "http://vue-loader.vuejs.org/",
"target": "_blank"
}
}, [_vm._v("vue-loader")])]), _vm._v(" "), _c('li', [_c('a', {
attrs: {
"href": "https://github.com/vuejs/awesome-vue",
"target": "_blank"
}
}, [_vm._v("awesome-vue")])])])
}]
var esExports = {
render: render,
staticRenderFns: staticRenderFns
}
/* harmony default export */
__webpack_exports__["a"] = (esExports);
/***/
})
總結(jié)
結(jié)構(gòu)梳理,一圖勝千言

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
nuxt框架中對vuex進行模塊化設置的實現(xiàn)方法
這篇文章主要介紹了nuxt框架中對vuex進行模塊化設置的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-09-09
v-for循環(huán)中使用require/import關鍵字引入本地圖片的幾種方式
在做項目的過程中,模版相同,可是不標題和圖片不同,循環(huán)標題我們知道可以用v-for循環(huán),可是該怎么引入本地圖片呢?下面這篇文章主要給大家介紹了v-for循環(huán)中使用require/import關鍵字引入本地圖片的幾種方式,需要的朋友可以參考下2021-09-09
element el-table如何實現(xiàn)表格動態(tài)增加/刪除/編輯表格行(帶校驗規(guī)則)
這篇文章主要介紹了element el-table如何實現(xiàn)表格動態(tài)增加/刪除/編輯表格行(帶校驗規(guī)則),本篇文章記錄el-table增加一行可編輯的數(shù)據(jù)列,進行增刪改,感興趣的朋友跟隨小編一起看看吧2024-07-07
vue如何通過ref調(diào)用router-view子組件的方法
這篇文章主要介紹了vue?通過ref調(diào)用router-view子組件的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-11-11
React組件通信之路由傳參(react-router-dom)
本文主要介紹了React組件通信之路由傳參(react-router-dom),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10
Vue3?vue-devtools?調(diào)試工具下載安裝使用教程
vue-devtools是一款基于chrome游覽器的插件,用于調(diào)試vue應用,這可以極大地提高我們的調(diào)試效率,尤其是對于初學vue的朋友來說可謂是一大利器,這篇文章主要介紹了Vue3?vue-devtools?調(diào)試工具下載安裝,需要的朋友可以參考下2022-08-08

