一、整体概述
使用过 Vue 的同学,对于 .vue 单文件文件组件类型的文件(下文简称 SFC)应该不会陌生。SFC 文件需要通过构建工具(本文以 Webpack4 为例)打包成一个 Bundle,才能被识别和使用。那么这中间经历了什么、不同的代码块是如何被其他规则识别的、最终生成了什么?带着这些问题,且看下文一一道来。
▲图 1. SFC 经过 Webpack 打包后的产物是什么
1. SFC 的输入和输出
Webpack 需要增加 vue-loader 【1】和 vueLoaderPlugin 对 SFC 进行支持。我们首先聚焦到 vue-loader 的代码:入口文件为 lib/index.js ,入参 source 是 SFC 源码,经过处理逻辑后,输出 export default 的代码字符串。
【1】:vue-loader:
https://github.com/vuejs/vue-loader/blob/master/lib/index.js#L32
用实际的例子操作一下, demo.vue 经过运行后得到下图的输出。经过观察可以发现,原有的 template ,变成了 import ... from './demo.vue?vue&type=template' ,其他代码块也发生了类似的变化。 ?vue&type=template 新增的 vue 和 type 参数的作用是什么?我们继续往下看。
▲图 2. SFC 被 vue-loader 转化后的结果
2. template、script、style 代码块切分
上个小节中,可以看到 template、script、style 代码块在输出结果中已经转化为对应的 import 逻辑。这一步是 vue-loader 调用了 @vue/component-compiler-utils 的 parse 函数进行解析后,分别生成了对应的 import 逻辑,相关源码如下:
下图是 demo.vue 被转化的流程图解:
▲图 3. 各个代码块被分别转化为相应的 import 逻辑
二、VueLoaderPlugin 的作用
前面的章节留了一个疑问, ?vue&type=template 的作用是什么?可以从 VueLoaderPlugin 【2】中找出答案,首先我们先了解 Webpack 中的 Plugin 能做什么。
【2】:VueLoaderPlugin:
https://github.com/vuejs/vue-loader/blob/master/lib/plugin-webpack4.js
Plugin 的特性
Plugin 的作用,主要有以下两条:
能够 hook 到在每个编译(compilation)中触发的所有关键事件。
在插件实例的 apply 方法中,可以通过 compiler.options 获取 Webpack 配置,并进行修改。
VueLoaderPlugin 通过第二个特性,在初始化阶段,对 module.rules 进行动态修改。
VueLoaderPlugin 预处理
VueLoaderPlugin 的处理流程中,修改了 module.rules,在原来的基础上加入了 pitcher 和 cloneRules 。这一步的作用是:新增的 rule ,能识别形如 ?vue&type=template 的 querystring,让不同语言的代码块匹配到对应的 rule。
【3】:对资源的 querystring 进行匹配:
https://v4.webpack.docschina.org/configuration/module/#rule-resourcequery
▲图 4. VueLoaderPlugin 对 module.rules 的修改
三、回到 Loader
上一节梳理了 VueLoaderPlugin 在初始化阶段的预处理,这一节我们继续回到构建阶段中,看看以 VueLoader 为中心如何协调其它 Loader ,得到每个代码块的构建结果。同样地,我们先了解一下 Webpack 的 loader 特性。
Webpack 的 loader 运行顺序
对于 loader ,我们知道它们的执行是有顺序的,如果是这样的配置,运行的顺序将是 c-loader -> b-loader -> a-loader。
不过,在实际(从右到左)执行 loader 之前,会先从左到右调用 loader 上的 pitch 方法。
并且在 loader 的 pitch 方法中,如果有实际的返回值,将会跳过后续的 loader,比如在 b-loader 的 pitch 中,如果返回了实际值,将会产生下面的执行顺序。
知道这个特性,有利于我们理解 SFC 中各代码块在 loader 中的处理顺序。
2. SFC 转化流程
还记得第一节生成的编译结果吗?每个代码块都导出了对应逻辑,我们以 script 块为例,结合第二节的 PitcherLoader 再次进行转化,转化后的结果为:
▲图 5. script 块的转化流程
最后的 import 语句,使用了内联方式的 import 语法【4】,我们拆分一下便于理解。
【4】:import 语法:
https://webpack.docschina.org/concepts/loaders/#inline
3. PitchLoader
上述的转化发生在 PitchLoader 中,对 PitchLoader 的实现逻辑感兴趣的同学,可以阅读 loader/pitcher.js 的源码:
4. 再次执行 VueLoader
细心的同学可能发现了,在 PitchLoader 的转化结果中,还是会以 vue-loader 作为第一个处理的 loader,但 vue-loader 不是一开始就转化过了吗 ?与第一次不同的是,这次 vue-loader 的作用,仅仅是把 SFC 中语法块的源码提取出来,并交给后面的 loader 进行处理。
▲图 6. 第二次进入 vue-loader
至此,vue-loader 里面的处理逻辑基本已经梳理完成。各部分代码块也传入后续的 loader 中进行解析和转化。
四、总结
我们再用一张完整的处理流程图总结一下 SFC 构建流程吧:
头图:Unsplash
作者:尹佳
原文:https://mp.weixin.qq.com/s/FJzDRLchG_DWA80Wp141Vg
原文:图解 VueLoader : .vue 文件是如何被打包的?
来源:云加社区 - 微信公众号 [ID:QcloudCommunity]
转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论