先整体过一遍 vue-cli4 处理单文件组件 (SFCs)的大致过程
webpack并不认识.vue
后缀的单文件组件,因此需要强大的 vue-loader 来处理。
首先,vue-loader 通过@vue/component-compiler-utils
解析.vue
源代码,为每个语言块生成一个导入(可以将每个块看作一个“虚拟模块”),这一步实际返回的模块代码看起来像这样:
// import <template> 块
import render from 'source.vue?vue&type=template'
// import <script> 块
import script from 'source.vue?vue&type=script'
export * from 'source.vue?vue&type=script'
// import <style> 块
import 'source.vue?vue&type=style&index=1'
script.render = render
export default script
VueLoaderPlugin
插件会基于webpack配置的每个模块规则, 创建针对 Vue 相应语言块请求的克隆版本。vue-cli 已经为.js
文件配置了 babel-loader 和 cache-loader,因此这个规则也会被复制并应用到导入的<script>
模块。同样地, import 的<style>
块会按需应用 sass-loader、css-loader、vue-style-loader 等预处理器、 <template>
的部分则会交由 pug-plain-loader 和 raw-loader 处理。
在webpack内部,一个import script from 'source.vue?vue&type=script'
的模块请求会被扩展为:
import script from 'babel-loader!vue-loader!source.vue?vue&type=script'
如果对 “用 import 方式指定文件loader相关规则” 不清楚,可以参阅【 webpack 之 Loader 详解 的 内联方式(inline) 部分】和 【loader使用之内联方式】。
相似地, 假设为 *.scss
文件配置了 style-loader + css-loader + sass-loader,
<style scoped lang="scss">
...
</style>
经由 vue-loader 解析后返回:
import 'source.vue?vue&type=style&index=1&scoped&lang=scss'
跟着被webpack扩展为:
import 'style-loader!css-loader!sass-loader!vue-loader!source.vue?vue&type=style&index=1&scoped&lang=scss'
像这样,vue-loader 经多重处理,使 Vue 组件同时可以使用其它loader,然后用自己的专用 loader 链处理每个语言块,最终将这些块组装到一个ES Module中。它的默认导出是一个包含 Vue.js 组件选项的对象。
<style>
和 <template>
中引用的资源会被当作模块依赖来处理
模版,即<template>
的内容将被提取并以模版字符串的形式传递给 vue-template-compiler
,预处理为 JavaScript 渲染函数,并最终注入到从 <script>
导出的组件中。
Vue Loader 编译<template>块时,会将遇到的资源URL转为 webpack 模块请求。即将资源作为模块 require() 进来。
比如模版中有如下代码片段,
<img src="../image.png">
会被编译成:
createElement('img', {
attrs: {
src: require('../image.png') // 现在这是一个模块的请求了
}
})
任何匹配 .css
(或通过 <style> 的 lang
特性指定的扩展名) 文件的 webpack 规则都将会运用到 <style>
块的内容中。
<style scoped>
块内部的 CSS 只作用于当前组件中的元素,在应用css-loader
之前,它会先通过postcss-loader
进行以下转换来实现样式的本地化:
<style scoped>
.example {
color: red;
}
</style>
<template>
<div class="example">hi</div>
</template>
转换结果:
<style>
.example[data-v-f3f3eg9] {
color: red;
}
</style>
<template>
<div class="example" data-v-f3f3eg9>hi</div>
</template>
<style>
块都会经过 css-loader 处理,来把其中 @import 和 url() 的资源URL转为模块请求。如果是<style lang='sass'>
,在 css-loader 之前还要先执行 sass-loader,把 sass 文件转换 成 css。
// 把 `@import` 或`url()` 变成 `require()`
url(../image.png) => require('../image.png')
webpack 会在 import 或 "load(加载)" 模块时预处理文件。而资源URL经解析转换后指向的文件(如.png)仍然不是js模块,就需要 url-loader
和 file-loader
进一步处理了。
vue-cli4 的默认配置中,当资源小于4kb( url-loader 配置项 limit
定义的值 )就会被转换成内联的 base-64 URL,这会大大减少小文件的 HTTP 请求数。而如果文件大于该阈值,会自动交给 file-loader 处理。
源码详见./node_modules/@vue/cli-service/lib/config/base.js
file-loader 可以指定要复制/放置资源文件的目标位置,以及使用版本哈希命名这些文件以获得更好的缓存。此外,还会在打包输出中自动重写文件路径为正确的 URL(文件访问路径)。这意味着你可以按自己的喜好管理输出文件的目录或文件名,并且可以在开发时使用相对路径,完全不用担心部署时 URL 会出问题。
样式的最后处理:非生产环境,vue-style-loader
会往 head 标签中注入多个 style 标签。而生产环境,mini-css-extract-plugin
插件会将 CSS 样式提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 .css 文件,并且支持 CSS 和 SourceMaps 的按需加载。
关于 file-loader 的具体配置,请移步 file-loader 配置详解以及资源相对路径处理
网友评论