上一篇里面对VueLoaderPlugin的实现有了一个大体的思路,其中有一段代码当时没看明白。是通过调用一个createMatcher去匹配.vue和.vue.html文件类型的规则。然后会判断这个规则是否存在,再去找这些规则里面去找vue-loader的规则。找到后给这个规则添加option和ident(用于其他loader获取当前loader中options里的配置,比如template-loader??vue-loader-options)。然后接下来就是匹配规则,覆盖规则的操作。
1.vue-loader
以下是我现在能理解的流程
module.exports = function (source) {
const loaderContext = this
//thread-loader和VueLoaderPlugin是否加载完成
if (!errorEmitted && !loaderContext['thread-loader'] && !loaderContext[NS]) {
loaderContext.emitError(new Error(
`vue-loader was used without the corresponding plugin. ` +
`Make sure to include VueLoaderPlugin in your webpack config.`
))
errorEmitted = true
}
...
//query解析,options解析,参数获取处理等
const rawQuery = resourceQuery.slice(1)
const inheritQuery = `&${rawQuery}`
const incomingQuery = qs.parse(rawQuery)
const options = loaderUtils.getOptions(loaderContext) || {}
const isServer = target === 'node'
const isShadow = !!options.shadowMode
const isProduction = options.productionMode || minimize || process.env.NODE_ENV === 'production'
const filename = path.basename(resourcePath)
const context = rootContext || process.cwd()
const sourceRoot = path.dirname(path.relative(context, resourcePath))
//这里用的@vue/component-compiler-utils里面的parse去生成descriptor对象
const descriptor = parse({
source,
compiler: options.compiler || loadTemplateCompiler(loaderContext),
filename,
sourceRoot,
needMap: sourceMap
})
// if the query has a type field, this is a language block request
// e.g. foo.vue?type=template&id=xxxxx
// and we will return early
//如果请求里存在type说明这是一个块请求
if (incomingQuery.type) {
//select.js里面有template,script,style,custom这几个不同的块处理
//取descriptor对象里面的内容
return selectBlock(
descriptor,
loaderContext,
incomingQuery,
!!options.appendExtension
)
}
//如果不存在type就说明是vue请求(不是解析后的块)
//接下来会生成template,script,styles模版
//如果有自定义块还会生成custom模版
//热更新配了还会生成热更新模版,然后拼接返回。
// module id for scoped CSS & hot-reload
const rawShortFilePath = path
.relative(context, resourcePath)
.replace(/^(\.\.[\/\\])+/, '')
const shortFilePath = rawShortFilePath.replace(/\\/g, '/') + resourceQuery
const id = hash(
isProduction
? (shortFilePath + '\n' + source)
: shortFilePath
)
// template
let templateImport = `var render, staticRenderFns`
let templateRequest
if (descriptor.template) {
const src = descriptor.template.src || resourcePath
const idQuery = `&id=${id}`
const scopedQuery = hasScoped ? `&scoped=true` : ``
const attrsQuery = attrsToQuery(descriptor.template.attrs)
const query = `?vue&type=template${idQuery}${scopedQuery}${attrsQuery}${inheritQuery}`
const request = templateRequest = stringifyRequest(src + query)
templateImport = `import { render, staticRenderFns } from ${request}`
}
...
//最后,单个文件加载后会触发判断type这一步。