本文介绍了webpack编译vue项目生成的代码探索,分享给大家,具体如下:
前言
往 main.js 里写入最简单的 vue 项目结构如下
import Vue from 'vue';
import App from './App.vue';
new Vue({
el: '#app',
template: '',
components: {
App
}
})
App.vue 如下
{{ msg }}
Essential links
-
Core Docs
-
Forum
-
Community Chat
-
Twitter
Ecosystem
-
vue-router
-
vuex
-
vue-loader
-
awesome-vue
#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;
}
编译生成后得到一个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
(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实例对象
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
var __WEBPACK_importED_MODULE_0_vue__ = __webpack_require__(2);
var __WEBPACK_importED_MODULE_1__App_vue__ = __webpack_require__(6);
// 从2号模块导出的一个叫a的变量,就是Vue对象本身
new __WEBPACK_importED_MODULE_0_vue__["a" ]({
el: '#app',
template: '',
components: {
App: __WEBPACK_importED_MODULE_1__App_vue__["a" ]
}
});
})
2号模块
即是 Vue 源码本身,从114行一直到了10818行,一共是10705行代码,啧啧啧
webpack 有所配置,所以导出的 Vue 实际上是 vue/dist/vue.esm.js 的完整编译版本。
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
// 作用域指向__webpack_exports__,并把__webpack_require__(0)作为global,实际上就是window
// __webpack_require__(3).setImmediate)作为setsetImmediate参数传入函数
(function (global, setImmediate) {
// 省略近1w行的代码,关于vue原本本身的解读以后再做......
// 最终 export 出来一个叫 Vue$3的对象
__webpack_exports__["a"] = (Vue$3);
}.call(__webpack_exports__, __webpack_require__(0), __webpack_require__(3).setImmediate))
}),
3,4,5号模块
都和 node_modules/setimmediate 有关,由于 vue 的 DOM 异步更新机制使用到了它,所以被引入。
这里也不做详解,只给出结构。
(function (module, exports, __webpack_require__) {
// 省略代码...
// setimmediate attaches itself to the global object
__webpack_require__(4);
exports.setImmediate = setImmediate;
exports.clearImmediate = clearImmediate;
}),
(function (module, exports, __webpack_require__) {
(function (global, process) {
// 省略代码...
}.call(exports, __webpack_require__(0), __webpack_require__(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中。
(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)
var __vue_template_functional__ = false
var __vue_styles__ = injectStyle
var __vue_scopeId__ = null
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" ],
__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" ],
__vue_template_functional__,
__vue_styles__,
__vue_scopeId__,
__vue_module_identifier__
)
__webpack_exports__["a"] = (Component.exports);
}),
7、8、9、10、11
都和样式有关,简言之就是7号模块加载8号模块获取css代码作为参数,并作为参数传入10号模块进行插入
太长也只大意上列出结构
- 7号模块由 style-loader 带入,把所有的css插入到 style 标签里
- 8号模块加载具体的css代码,
- 9号模块由css-loader代入,用于做css的sourceMap
- 10号模块返回具体插入动作函数,供7号模块调用
- 11号模块把所有的样式组成的数组转为字符串,给10号模块做插入。
(function (module, exports, __webpack_require__) {
// style-loader: Adds some css to the DOM by adding a 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);
}),
(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
}),
(function (module, exports) {
// css base code, injected by the css-loader
module.exports = function (useSourceMap) {
// 省略代码...
}
}),
(function (module, exports, __webpack_require__) {
// ...太长只贴了关键步骤,总之关键的函数就是这些
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 ) {
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 ) {
// ...
styleElement = createStyleElement()
// ...
}
}),
(function (module, exports) {
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 函数。
(function (module, exports) {
// 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
) {
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
}
}
}),
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
// 这就是 App.vue 中 script 的部分
__webpack_exports__["a"] = ({
name: 'app',
data: function data() {
return {
msg: 'Welcome to Your Vue.js App'
};
}
});
}),
(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('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
}
__webpack_exports__["a"] = (esExports);
})
总结
结构梳理,一图胜千言
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。



