vue-loader(二)

300 阅读1分钟

上一篇里面对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这一步。