您好,登錄后才能下訂單哦!
本文介紹了webpack編譯vue項目生成的代碼探索,分享給大家,具體如下:
前言
往 main.js 里寫入最簡單的 vue 項目結構如下
import Vue from 'vue'; import App from './App.vue'; new Vue({ el: '#app', template: '<App/>', components: { App } })
App.vue 如下
<template> <div id="app"> <h2>{{ msg }}</h2> <h3>Essential Links</h3> <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> <h3>Ecosystem</h3> <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; } h2, h3 { 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
代碼分解
按順序往下解讀,本篇編譯后的代碼在這兒,如果只想看結論那么請拉到最后有一張結構梳理圖。
webpack 模塊機制
前面70行還是熟悉的 webpack 模塊機制的基礎代碼,關于它的細致解讀參見上一篇webpack模塊機制,編譯后的代碼格式如下,并且我做了代碼美化,并且插上了中文注釋
/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ // 緩存模塊,所有被加載過的模塊都會成為installedModules對象的屬性,靠函數__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, 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 執行第一個依賴模塊并且返回它輸出。 /******/ 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參數傳入函數 (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 異步更新機制使用到了它,所以被引入。
這里也不做詳解,只給出結構。
/* 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 標簽內的樣式插入到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 函數。 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號模塊進行插入
太長也只大意上列出結構
/* 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}h2,h3{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 */ // ...太長只貼了關鍵步驟,總之關鍵的函數就是這些 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])) // .... } } // 總之先調用了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 函數。
/* 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 函數,扔給vue處理最終編譯成Vnode節點在渲染成DOM輸出到視圖 var render = function () { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; return _c('div', { attrs: { "id": "app" } }, [_c('h2', [_vm._v(_vm._s(_vm.msg))]), _vm._v(" "), _c('h3', [_vm._v("Essential Links")]), _vm._v(" "), _vm._m(0, false, false), _vm._v(" "), _c('h3', [_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); /***/ })
總結
結構梳理,一圖勝千言
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。