对vueloader的研究
vue-loader
是webpack的加载器,允许您以称为单文件组件(SFC)的格式创作Vue组件:
<template>
<div class="example">{{ msg }}</div>
</template>
<script>
export default {
data () {
return {
msg: 'Hello world!'
}
}
}
</script>
<style>
.example {
color: red;
}
</style>
有许多很酷的功能提供vue-loader
:
- 允许对Vue组件的每个部分使用其他webpack加载器,例如Sass for
<style>
和Pug for<template>
; - 允许
.vue
文件中的自定义块可以应用自定义加载器链; - 处理在模块依赖项中引用的静态资产,
<style>
并<template>
使用webpack加载器处理它们; - 模拟每个组件的范围CSS;
- 在开发过程中保持国家的热再加载。
简而言之,webpack的组合vue-loader
为您提供了一个现代,灵活且极其强大的前端工作流程,用于创作Vue.js应用程序。
Vue的CLI
如果您对手动设置webpack不感兴趣,建议使用Vue CLI构建项目。Vue CLI创建的项目预先配置了大多数开箱即用的常见开发需求。
如果Vue CLI的内置配置不符合您的需要,请遵循本指南,或者您更愿意从头开始创建自己的webpack配置。
#手动配置
Vue Loader的配置与其他加载器略有不同。除了适用vue-loader
于任何带扩展名的文件的规则外,请.vue
务必将Vue Loader的插件添加到webpack配置中:
// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [
// ... other rules
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// make sure to include the plugin!
new VueLoaderPlugin()
]
}
这个插件是必需的!它负责克隆您定义的任何其他规则并将它们应用于.vue
文件中的相应语言块。例如,如果您有规则匹配/\.js$/
,则它将应用于文件中的<script>
块.vue
。
一个更完整的webpack配置示例如下所示:
// webpack.config.js
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
mode: 'development',
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
// this will apply to both plain `.js` files
// AND `<script>` blocks in `.vue` files
{
test: /\.js$/,
loader: 'babel-loader'
},
// this will apply to both plain `.css` files
// AND `<style>` blocks in `.vue` files
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
}
]
},
plugins: [
// make sure to include the plugin for the magic
new VueLoaderPlugin()
]
}
资产URL处理
当Vue Loader <template>
在SFC中编译块时,它还会将遇到的任何资产URL转换为webpack模块请求。
例如,以下模板代码段:
<img src="../image.png">
将编译成:
createElement('img', {
attrs: {
src: require('../image.png') // this is now a module request
}
})
默认情况下,将转换以下标记/属性组合,并可使用transformAssetUrls选项进行配置。
{
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
此外,如果您已配置为块使用css-loader,则<style>
CSS中的资产URL也将以类似的方式处理。
转换规则
资产URL转换遵循以下规则:
-
如果URL是绝对路径(例如
/images/foo.png
),则它将按原样保留。 -
如果URL以#开头
.
,则将其解释为相对模块请求,并根据文件系统上的文件夹结构进行解析。 -
如果URL以...开头
~
,则将其解释为模块请求。这意味着您甚至可以引用节点模块内的资产:<img src="~some-npm-package/foo.png">
-
如果URL以
@
,它也被解释为模块请求。如果您的webpack配置具有别名@
,这非常有用,默认情况下,该别名指向/src
由其创建的任何项目vue-cli
。
#相关装载机
因为扩展.png
不是JavaScript模块,所以您需要配置webpack以使用file-loader或url-loader来正确处理它们。使用Vue CLI创建的项目已预先配置。
#为什么
资产URL转换的好处是:
-
file-loader
允许您指定复制和放置资产文件的位置,以及如何使用版本哈希命名它以获得更好的缓存。此外,这也意味着您只需将图像放在*.vue
文件旁边,并根据文件夹结构使用相对路径,而不必担心部署URL。通过适当的配置,webpack将自动将文件路径重写为捆绑输出中的正确URL。 -
url-loader
允许您有条件地将文件内联为base-64数据URL(如果它们小于给定阈值)。这可以减少普通文件的HTTP请求数量。如果文件大于阈值,则会自动回退到file-loader
。
使用预处理器
在webpack中,所有预处理器都需要应用相应的加载器。vue-loader
允许您使用其他webpack加载器来处理Vue组件的一部分。它将根据lang
语言块的属性和webpack配置中的规则自动推断要使用的正确加载器。
#SASS
例如,要<style>
使用SASS / SCSS 编译我们的标记:
npm install -D sass-loader node-sass
在您的webpack配置中:
module.exports = {
module: {
rules: [
// ... other rules omitted
// this will apply to both plain `.scss` files
// AND `<style lang="scss">` blocks in `.vue` files
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
}
]
},
// plugin omitted
}
现在除了能够之外import 'style.scss'
,我们还可以在Vue组件中使用SCSS:
<style lang="scss">
/* write SCSS here */
</style>
块中的任何内容都将由webpack处理,就像它在*.scss
文件中一样。
#上海社会科学院VS SCSS
请注意,默认情况下sass-loader
处理非基于缩进的scss
语法。要使用基于缩进的sass
语法,您需要将选项传递给加载器:
// webpack.config.js -> module.rules
{
test: /\.sass$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
indentedSyntax: true
}
}
]
}
共享全局变量
sass-loader
还支持一个data
选项,允许您在所有已处理文件之间共享公共变量,而无需显式导入它们:
// webpack.config.js -> module.rules
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
// you can also read from a file, e.g. `variables.scss`
data: `$color: red;`
}
}
]
}
LESS
npm install -D less less-loader
// webpack.config.js -> module.rules
{
test: /\.less$/,
use: [
'vue-style-loader',
'css-loader',
'less-loader'
]
}
手写笔
npm install -D stylus stylus-loader
// webpack.config.js -> module.rules
{
test: /\.styl(us)?$/,
use: [
'vue-style-loader',
'css-loader',
'stylus-loader'
]
}
PostCSS
小费
Vue Loader v15默认不再适用PostCSS变换。您需要使用PostCSS postcss-loader
。
npm install -D postcss-loader
// webpack.config.js -> module.rules
{
test: /\.css$/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: { importLoaders: 1 }
},
'postcss-loader'
]
}
PostCSS的配置可以通过postcss.config.js
或postcss-loader
选项完成。有关详细信息,请参阅postcss-loader docs。
postcss-loader
也可以与上述其他预处理器结合使用。
#巴贝尔
npm install -D babel-core babel-loader
// webpack.config.js -> module.rules
{
test: /\.js?$/,
loader: 'babel-loader'
}
Babel的配置可以通过.babelrc
或babel-loader
选项完成。
#不包括node_modules
exclude: /node_modules/
对于babel-loader
适用于.js
文件的JS转换规则(例如)通常是常见的。由于v15的推断变化,如果在内部导入Vue SFC node_modules
,其<script>
部分也将被排除在转换之外。
为了确保将JS转换应用于Vue SFC node_modules
,您需要使用exclude函数将它们列入白名单:
{
test: /\.js$/,
loader: 'babel-loader',
exclude: file => (
/node_modules/.test(file) &&
!/\.vue\.js/.test(file)
)
}
#打字稿
npm install -D typescript ts-loader
// webpack.config.js
module.exports = {
resolve: {
// Add `.ts` as a resolvable extension.
extensions: ['.ts', '.js']
},
module: {
rules: [
// ... other rules omitted
{
test: /\.ts$/,
loader: 'ts-loader',
options: { appendTsSuffixTo: [/\.vue$/] }
}
]
},
// ... plugin omitted
}
TypeScipt的配置可以通过tsconfig.json
。另请参阅ts-loader的文档。
帕格
处理模板有点不同,因为大多数webpack模板加载器如pug-loader
返回模板函数而不是编译的HTML字符串。pug-loader
我们需要使用一个返回原始HTML字符串的加载器,而不是使用,例如pug-plain-loader
:
npm install -D pug pug-plain-loader
// webpack.config.js -> module.rules
{
test: /\.pug$/,
loader: 'pug-plain-loader'
}
然后你可以写:
<template lang="pug">
div
h1 Hello world!
</template>
如果您还打算使用它.pug
在JavaScript中将文件作为HTML字符串导入,则需要raw-loader
在预处理加载器之后进行链接。但请注意,添加raw-loader
会破坏Vue组件中的使用,因此您需要有两个规则,其中一个使用a定位Vue文件resourceQuery
,另一个(回退)定位JavaScript导入:
// webpack.config.js -> module.rules
{
test: /\.pug$/,
oneOf: [
// this applies to `<template lang="pug">` in Vue components
{
resourceQuery: /^\?vue/,
use: ['pug-plain-loader']
},
// this applies to pug imports inside JavaScript
{
use: ['raw-loader', 'pug-plain-loader']
}
]
}
作用域CSS
当<style>
标签具有该scoped
属性时,其CSS将仅应用于当前组件的元素。这类似于Shadow DOM中的样式封装。它有一些警告,但不需要任何polyfill。通过使用PostCSS转换以下内容来实现:
<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>
/* global styles */
</style>
<style scoped>
/* local styles */
</style>
#子组件根元素
使用时scoped
,父组件的样式不会泄漏到子组件中。但是,子组件的根节点将受父级作用域CSS和子级作用域CSS的影响。这是设计的,以便父级可以设置子根元素的样式以进行布局。
#深度选择器
如果您希望scoped
样式中的选择器“深入”,即影响子组件,则可以使用>>>
组合器:
<style scoped>
.a >>> .b { /* ... */ }
</style>
以上将编译成:
.a[data-v-f3f3eg9] .b { /* ... */ }
某些预处理器(如Sass)可能无法>>>
正确解析。在这些情况下,您可以使用/deep/
组合器 - 它是别名,>>>
并且完全相同。
#动态生成的内容
创建的DOM内容v-html
不受范围样式的影响,但您仍然可以使用深度选择器设置它们的样式。
#也记住
-
范围样式不会消除类的需要。由于浏览器呈现各种CSS选择器的方式,
p { color: red }
在作用域时(即与属性选择器结合时)会慢很多倍。如果您使用类或ID,例如in.example { color: red }
,那么您几乎可以消除性能损失。这是一个游乐场,您可以自己测试差异。 -
在递归组件中注意后代选择器!对于带有选择器的CSS规则
.a .b
,如果匹配的元素.a
包含递归子组件,则.b
该子组件中的所有组件都将与规则匹配。
← 简介
CSS模块
CSS模块是用于模块化和组合CSS的流行系统。vue-loader
提供与CSS模块的一流集成,作为模拟范围CSS的替代方案。
#用法
首先,必须通过传递modules: true
给css-loader
:
// webpack.config.js
{
module: {
rules: [
// ... other rules omitted
{
test: /\.css$/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
// enable CSS Modules
modules: true,
// customize generated class names
localIdentName: '[local]_[hash:base64:8]'
}
}
]
}
]
}
}
然后,将module
属性添加到您的<style>
:
<style module>
.red {
color: red;
}
.bold {
font-weight: bold;
}
</style>
该module
属性指示Vue Loader将CSS模块locals对象作为具有名称的计算属性注入组件$style
。然后,您可以在模板中使用动态类绑定:
<template>
<p :class="$style.red">
This should be red
</p>
</template>
由于它是一个计算属性,它也可以使用以下对象/数组语法:class
:
<template>
<div>
<p :class="{ [$style.red]: isRed }">
Am I red?
</p>
<p :class="[$style.red, $style.bold]">
Red and bold
</p>
</div>
</template>
您还可以从JavaScript访问它:
<script>
export default {
created () {
console.log(this.$style.red)
// -> "red_1VyoJ-uZ"
// an identifier generated based on filename and className.
}
}
</script>
有关模式详细信息(如全局异常和组合),请参阅CSS模块规范。
#选择使用
如果您只想在某些Vue组件中使用CSS模块,则可以使用oneOf
规则并检查module
字符串resourceQuery
:
// webpack.config.js -> module.rules
{
test: /\.css$/,
oneOf: [
// this matches `<style module>`
{
resourceQuery: /module/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[local]_[hash:base64:5]'
}
}
]
},
// this matches plain `<style>` or `<style scoped>`
{
use: [
'vue-style-loader',
'css-loader'
]
}
]
}
#与预处理器一起使用
CSS模块可以与其他预处理器一起使用:
// webpack.config.js -> module.rules
{
test: /\.scss$/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: { modules: true }
},
'sass-loader'
]
}
#自定义进样名称
您可以<style>
在单个*.vue
组件中包含多个标记。为避免注入样式相互覆盖,可以通过为module
属性赋值来自定义注入的计算属性的名称:
<style module="a"> /* identifiers injected as a */ </style> <style module="b"> /* identifiers injected as b */ </style>
热刷新
“Hot Reload”不仅仅是在编辑文件时重新加载页面。启用热重新加载后,在编辑*.vue
文件时,将交换该组件的所有实例,而无需重新加载页面。它甚至可以保留您的应用程序和这些交换组件的当前状态!当您调整组件的模板或样式时,这可以显着改善开发体验。
#州保存规则
-
编辑
<template>
组件时,已编辑组件的实例将重新呈现,保留所有当前的私有状态。这是可能的,因为模板被编译成新的渲染函数,不会产生副作用。 -
编辑
<script>
组件的一部分时,将销毁并重新创建已编辑组件的实例。(保留应用程序中其他组件的状态)这是因为<script>
可能包含可能产生副作用的生命周期钩子,因此需要“重新加载”而不是重新呈现以确保一致的行为。这也意味着您需要注意全局副作用,例如组件生命周期钩子中的计时器。如果您的组件产生全局副作用,有时您可能需要执行整页重新加载。 -
<style>
热重载依次运行vue-style-loader
,因此不会影响应用程序状态。
#用法
搭建项目时vue-cli
,Hot Reload可以开箱即用。
手动设置项目时,在使用项目服务时会自动启用热重新加载webpack-dev-server --hot
。
高级用户可能想要查看内部使用的vue-hot-reload-apivue-loader
。
#禁用热重新加载
除以下情况外,始终启用热重新加载:
- webpack
target
是node
(SSR) - webpack缩小了代码
process.env.NODE_ENV === 'production'
您可以使用hotReload: false
选项明确禁用热重新加载:
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
hotReload: false // disables Hot Reload
}
}
]
}
功能组件
在*.vue
文件中定义为单文件组件的功能组件也接收适当的模板编译,Scoped CSS和热重载支持。
要表示应编译为功能组件functional
的模板,请将该属性添加到模板块。这也允许省略块中的functional
选项<script>
。
模板中的表达式在功能渲染上下文中进行评估。这意味着需要像props.xxx
在模板中一样访问props :
<template functional>
<div>{{ props.foo }}</div>
</template>
如果需要访问全局定义的属性Vue.prototype
,可以在parent
以下位置访问它们:
<template functional>
<div>{{ parent.$someProperty }}</div>
</template>
自定义块
您可以在*.vue
文件中定义自定义语言块。应用于自定义块的加载器将根据块的lang
属性,块的标记名称以及webpack配置中的规则进行匹配。
如果lang
指定了属性,则自定义块将作为文件lang
与其扩展名匹配。
您还可以使用resourceQuery
匹配自定义块的规则lang
。例如,要匹配<foo>
自定义块:
{
module: {
rules: [
{
resourceQuery: /blockType=foo/,
loader: 'loader-to-use'
}
]
}
}
如果为自定义块找到匹配规则,则会对其进行处理; 否则将自动忽略自定义块。
此外,如果自定义块在所有匹配的加载器处理后将函数导出为最终结果,则将使用*.vue
文件的组件作为参数调用该函数。
#例子
下面是将<docs>
自定义块注入组件的示例,以便在运行时可用。
为了注入自定义块内容,我们将编写一个自定义加载器:
module.exports = function (source, map) {
this.callback(
null,
`export default function (Component) {
Component.options.__docs = ${
JSON.stringify(source)
}
}`,
map
)
}
现在我们将配置webpack以使用我们的自定义加载器来<docs>
定制块。
// wepback.config.js
module.exports = {
module: {
rules: [
{
resourceQuery: /blockType=docs/,
loader: require.resolve('./docs-loader.js')
}
]
}
}
我们现在能够<docs>
在运行时访问块的导入组件的内容。
<!-- ComponentB.vue -->
<template>
<div>Hello</div>
</template>
<docs>
This is the documentation for component B.
</docs>
<!-- ComponentA.vue -->
<template>
<div>
<ComponentB/>
<p>{{ docs }}</p>
</div>
</template>
<script>
import ComponentB from './ComponentB.vue';
export default {
components: { ComponentB },
data () {
return {
docs: ComponentB.__docs
}
}
}
</script>
CSS提取
小费
仅对生产应用CSS提取,以便在开发期间获得CSS热重新加载。
#的WebPack 4
npm install -D mini-css-extract-plugin
// webpack.config.js
var MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// other options...
module: {
rules: [
// ... other rules omitted
{
test: /\.css$/,
use: [
process.env.NODE_ENV !== 'production'
? 'vue-style-loader'
: MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
// ... Vue Loader plugin omitted
new MiniCssExtractPlugin({
filename: 'style.css'
})
]
}
另请参阅mini-css-extract-plugin文档。
#的WebPack 3
npm install -D extract-text-webpack-plugin
// webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin")
module.exports = {
// other options...
module: {
rules: [
// ... other rules omitted
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
use: 'css-loader',
fallback: 'vue-style-loader'
})
}
]
},
plugins: [
// ... Vue Loader plugin omitted
new ExtractTextPlugin("style.css")
]
}
Linting
#ESLint
The official eslint-plugin-vue supports linting both the template and script parts of Vue single file components.
Make sure to use the plugin's included config in your ESLint config:
// .eslintrc.js
module.exports = {
extends: [
"plugin:vue/essential"
]
}
Then from the command line:
eslint --ext js,vue MyComponent.vue
Another option is using eslint-loader so that your *.vue
files are automatically linted on save during development:
npm install -D eslint eslint-loader
Make sure it's applied as a pre-loader:
// webpack.config.js
module.exports = {
// ... other options
module: {
rules: [
{
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /node_modules/
}
]
}
}
#stylelint
stylelint supports linting style parts of Vue single file components.
Make sure that your stylelint config is right.
Then from the command line:
stylelint MyComponent.vue
Another option is using stylelint-webpack-plugin:
npm install -D stylelint-webpack-plugin
Make sure it's applied as a plugin:
// webpack.config.js
const StyleLintPlugin = require('stylelint-webpack-plugin');
module.exports = {
// ... other options
plugins: [
new StyleLintPlugin({
files: ['**/*.{vue,htm,html,css,sss,less,scss,sass}'],
})
]
}
测试
-
Vue CLI提供预配置的单元测试和e2e测试设置。
-
如果您对手动设置
*.vue
文件的单元测试感兴趣,请参阅@ vue / test-utils的文档,其中介绍了使用mocha-webpack或Jest进行设置。