什么是 vue-loader
简单来说,vue-loader 是把 .Vue 文件编译成 .js,即可在浏览器中运行,也可以通过 vue-server-render 在 node 环境运行。
vue-loader 15
vue-loader 15 向较于过去的版本,有许多重要的改动,这些改动体现在:
- loader推导策略变化
- 独立出
VueLoaderPlugin - …等等
更多细节可以查阅官方迁移指南:Vue Loader 迁移
vue-loader 编译过程
vue-loader 的处理流程可以大致分为几个部分
- 入口函数解析
.vue文件 parse解析.vue文件,生成包含不同模块的descriptor- 根据不同模块做
loader推导 VueLoaderPlugin处理
vue-loader 入口函数
vue-loader 入口代码不多,我把入口函数的流程做成了一个简单的 UML 图,通过图也能快速对流程有个初步的印象

vue-loader 入口函数主要做了这几件事
- 通过
parse生成descriptor,描述符中包含了vue解析后的各个结果,比如template、style、script - 如果
resourceQuery中有type说明已经被处理过,可以进行分块处理,是loader推导策略的关键步骤点之一 - 生成
code,这里的code包含 import vue 文件的代码,并且引入携带不同类型的type,会触发新的vue-loader新一轮的执行,是loader推导策略的关键步骤点之一
通过上面的 UML 图可知,.vue 文件初次编译时会走生成 code的流程,那么生成的 code 究竟是什么呢?
通过调试 vue-loader,把 code 打印出来,仔细看下图中红色框中的部分

可以发现几句 import 中,都是从 source.vue 获取对象,并且路径上携带了参数,这些参数就是 resourceQuery,type有三种不同类型,分别是 template | script | styles
这些import会继续触发新一轮的 vue-loader 执行(简单来说,可以先这么理解,详细过程在下面会分析),于是接下来到了途中 resourceQuery 有 type 的情况
下面是进行了适当删减后的源码,把上述涉及到的代码做了保留,对代码本身感兴趣的可以浏览
1 | module.exports = function (source) { |
parse .vue组件解析
parse 方法内部处理了 vue SFC 文件,前面提到过,编译的方法默认是通过 vue-template-compiler 处理
主要是通过 compiler.parseComponent 函数对 .vue 文件进行编译
那么 vue-template-compiler 究竟是什么呢?
vue-template-compiler
在了解 vue-tempalte-compiler 之前,我对 vue 的编译过程有些了解,既然它们都是处理 vue SFC 文件,那么它们会不会是同一份代码呢,抱着疑问的态度,我们先看看 vue-template-compiler 的 readme.md
This package is auto-generated. For pull requests please see src/platforms/web/entry-compiler.js.
在 readme.md 可以看到官方对它的说明,实际 vue-template-compiler 是一份自动生成的代码,它本质就是 vue 中的 sfc/parse

但今天的主角并不是 vue-template-compiler,也不是 sfc/parse,我会在后面的篇章中对 vue build 的过程做一个详细的解读
parse 流程

vue-loader 推导策略
在 vue-loader 入口函数分析中已经可以了解到,入口函数最终会生成一个 code,这个 code 包含了几个 import 语句,import 语句都含有 vue 标识并且标明了不同的分块类型
这些 import 语句会被 VueLoaderPlugin 捕捉并做推导策略处理
VueLoaderPlugin
老规矩,先来看 VueLoaderPlugin 的代码
代码删减后及其简单,就一件事:注入 pitcher-loader,用于处理 vue 分块 loader 推导
1 | class VueLoaderPlugin { |
pitcher-loader
VueLoaderPlugin 的主要作用就是注入 pitcher-loader,由此可先,实际处理推导过程的是 pitcher-loader,VueLoaderPlugin 不过是loader的注入器
那么 pitcher-loader 是怎么做 loader 推导的呢?
前面提到 入口函数生成的 code,code 中包含 import 语句
这些 import 语句会触发 pitcher-loader,pitcher 根据resourceQuery来区分不同块,并生成不同的 loader request
1 | module.exports.pitch = function (remainingRequest) { |
loader 推导流程

总结
把上述过程汇聚成一张 UML 图,通过这张图可以对整个流程有个清晰认识

vue-loader15 的整体过程可以划分为一下几个部分
- 通过
parse生成descriptor,描述符中包含了vue解析后的各个结果,比如template、style、script - 如果
resourceQuery中有type说明已经被处理过,可以进行分块处理,是loader推导策略的关键步骤点之一 - 生成
code,这里的code包含 import vue 文件的代码,并且引入携带不同类型的type,会触发新的pitcher-loader的执行,是loader推导策略的关键步骤点之一 pitcher-loader生成的loader request会触发vue-loader新一轮的执行,这时resourceQuery中会包含type=xxxcode中的import都处理完后,当前的.vue sfc就处理完了