问题描述
主要记录一下开发过程中遇到的一次vue-loader加载异常问题。问题描述:
当前有A、B、C 3个.vue文件,其中在A里面局部注册了B组件。B 和 C 无论是文件内容、文件命名还是文件存储位置都没有半毛线关系。然而实际页面渲染的时候,将B组件渲染成了C。应用里其他用到B组件的地方也全部变成了C。
查看对应组件的js文件发现:
((typeof self !== 'undefined' ? self : this)["webpackJsonp"] = (typeof self !== 'undefined' ? self : this)["webpackJsonp"] || []).push([[15], {
/***/
"./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"433a206b-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/B/index.vue?vue&type=template&id=72514296&scoped=true&": /*!***********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
!*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"433a206b-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/B/index.vue?vue&type=template&id=72514296&scoped=true& ***!
\***********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
/*! exports provided: render, staticRenderFns */
/***/
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\",...));
/***/
}
),
}]);
发现其内部虽然指示了B组件,但是生成的render函数是C组件,所以最终渲染的也是C组件。
而上一段的render函数是在vue-loader的输出产物中依然发生了错误,所以我们需要查找vue-loader中可能造成该结果的地方。
问题排查
查找vue-loader源码,查看到以下这段代码
function parse(options) {
const { source, filename = '', compiler, compilerParseOptions = { pad: 'line' }, sourceRoot = '', needMap = true } = options;
const cacheKey = hash(filename + source + JSON.stringify(compilerParseOptions));
let output = cache.get(cacheKey);
if (output)
return output;
output = compiler.parseComponent(source, compilerParseOptions);
if (needMap) {
if (output.script && !output.script.src) {
output.script.map = generateSourceMap(filename, source, output.script.content, sourceRoot, compilerParseOptions.pad);
}
if (output.styles) {
output.styles.forEach(style => {
if (!style.src) {
style.map = generateSourceMap(filename, source, style.content, sourceRoot, compilerParseOptions.pad);
}
});
}
}
cache.set(cacheKey, output);
return output;
}
在上面的parse函数中,有一步缓存动作,该段代码是将文件名filename
,文件内容source
以及编译配置compilerParseOptions
一起生成一个hash值,理论上是想保证同一文件在同一编译配置下只编译一次。将编译后的结果存储在cache中,后续如果命中缓存,就存缓存中读取,提升编译效率。
但是问题就发生在了这一步缓存上,理论上不同文件对应的hash肯定不同。但是神奇的是,这俩文件B和C对应的hash是一样的,这就导致编译的时候先编译了C,后面再遇到B时,由于hash相同,直接从缓存cache中取出C的编译结果,导致B渲染成了C。这种概率真的算是很难装上了吧,大概堪比中彩票了。
解决问题
找到原因以后,问题就比较好解决了。由于是hash值撞了,只要修改文件内容或者文件名称,就能改变该文件对应的hash值。所以修改B或者C中任意一文件,随便加点什么注释空格啥的,就解决了该问题。
虽然问题不难解决,但是定位问题的过程还是挺麻烦的,还是请教了其他同事,才定位到vue-loader的parse函数的。不然真是一团乱,毕竟也没认真读过vue-loader的源码,简直毫无头绪。仅此记录,以供参考。虽然概率很低,万一有人遇到了,也不失一种借鉴