Storybook 不会在 Vue 项目的组件中加载 SVG
Storybook does not load SVGs in Components of Vue project
我用 vue-cli
创建了一个 Vue 项目,稍后通过 npm install --save-dev storybook
安装了 storybook
作为依赖项来显示我创建的组件。
故事书 webpack 配置如下所示:
const path = require('path')
module.exports = async ({ config, mode }) => {
config.module.rules.push(
{
test: /\.svg$/,
use: ['vue-svg-loader']
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
sourceMap: false,
data: '@import "./src/assets/styles/main.scss";'
}
}
],
include: path.resolve(__dirname, '../')
}
)
return config
}
故事 index.js
是这样的:
import Vue from 'vue';
import { storiesOf } from '@storybook/vue';
import Alert from '../src/components/buttons/Alert.vue';
storiesOf('Components', module)
.add('Alert', () => ({
components: { Alert },
template: '<Alert />'
}))
出于某种原因,当我尝试加载由 SVG 组成的组件时,我得到:
显示了组件本身,但没有显示 SVG 所在的部分。
有趣的是,尽管它在尝试在 Vue 的主应用程序中显示组件时工作得很好。 vue.config
看起来像这样:
module.exports = {
css: {
loaderOptions: {
sass: {
data: `@import "./src/assets/styles/main.scss";`
}
}
},
chainWebpack: config => {
const svgRule = config.module.rule("svg");
svgRule.uses.clear();
svgRule.use("vue-svg-loader").loader("vue-svg-loader");
}
};
为什么 storybook
无法正确加载 svg 而主 Vue 应用程序可以?
编辑: 我已经继续并简单地使用了 webpacks file-loader
以确保它与 vue-svg-loader
:
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {},
},
],
},
但我遇到了同样的错误。
在尝试应用第一个答案将 push()
替换为 unshift()
规则后,我收到此错误:Error in parsing SVG: Non-whitespace before first tag
.
编辑2:
这是我要加载的 SVG 文件之一:
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="exclamation-triangle" class="svg-inline--fa fa-exclamation-triangle fa-w-18" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"></path></svg>
Edit3: *Alert.vue in projectroot/src/components/banners/Alert.vue
, Storybook 的 Webpack 配置在 projectroot/.storybook/webpack.config.js
<template>
<div v-if="show" :class="`alert_wrap ${model}`">
<IconBase>
<component :is="iconComponent" />
</IconBase>
<div class="alert_text">
<p v-if="title">{{ title }}</p>
<p>{{ msg }}</p>
</div>
<IconBase>
<TimesIcon @click="show = !show" aria-hidden="Close" />
</IconBase>
</div>
</template>
<script>
import IconBase from "../icons/IconBase.vue";
import CautionIcon from "../../assets/icons/exclamation-triangle-solid.svg";
import InfoIcon from "../../assets/icons/info-circle-solid.svg";
import SuccessIcon from "../../assets/icons/check-circle-solid.svg";
import TimesIcon from "../../assets/icons/times-solid.svg";
export default {
name: "Alert",
components: {
CautionIcon,
InfoIcon,
SuccessIcon,
TimesIcon,
IconBase
},
props: {
msg: {
type: String,
required: true
},
title: {
type: String
},
model: {
type: String,
default: "info",
validator(type) {
return ["caution", "info", "success"].includes(type);
}
}
},
computed: {
iconComponent() {
return `${this.model}-icon`;
}
},
data() {
return {
show: true
};
}
};
</script>
<style scoped lang="scss">
/* some styles depending on the props passed */
</style>
因为 storybook
已经有一个 file-loader
可以将您的 svg
作为资产发出。我们应该首先从 file-loader
中排除 svg
。
// exclude svg from existing rule
config.module.rules = config.module.rules.map(rule => {
if (rule.loader && rule.loader.endsWith('file-loader')) {
return {
...rule,
test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/,
};
}
return rule;
});
// now we can add our svg loader
config.module.rules.push(
{
test: /\.svg$/,
use: ['vue-svg-loader']
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
sourceMap: false,
data: '@import "./src/assets/styles/main.scss";'
}
}
],
include: path.resolve(__dirname, '../')
}
)
您使用的是 UTF-8 编码吗?
你有
的错误
Error in parsing SVG: Non-whitespace before first tag
用于与使用 Byte-Order-Mark (BOM) 的编码相关。
"Zero width no-break space" unicode character which Windows systems
automatically prepend to utf-8 files
如果您使用十六进制编辑器检查文件,您可以看到罪魁祸首。
您可以在加载文件之前手动将编码更改为 "without BOM" 或使用 replace("\ufeff", "") 之类的内容进行替换。
如果您使用的是编辑器,那么您可能具有特定 "with BOM" 编码的配置。例如在记事本++
添加 jsx 选项的代码
loader: 'file-loader',
options: {
jsx: true,
},
@liximomo 的回答几乎是正确的。您应该从 file-loader
处理规则中删除 svg
文件,但规则搜索条件应该略有不同:
module.exports = ({ config }) => {
let rule = config.module.rules.find(r =>
// it can be another rule with file loader
// we should get only svg related
r.test && r.test.toString().includes('svg') &&
// file-loader might be resolved to js file path so "endsWith" is not reliable enough
r.loader && r.loader.includes('file-loader')
);
rule.test = /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/;
config.module.rules.push(
{
test: /\.svg$/,
use: ['vue-svg-loader']
}
)
// ...
return config;
}
另一种在不改变 Webpack 配置规则的情况下导入 SVG 的方法是指定一个 内联 加载器,当某些组件已经在 [=20] 中使用 SVG 时,这有助于避免错误=] 或 HTML 作为文件路径。示例:
// disable all rules with !! and use just vue-svg-loader
import CautionIcon from "!!vue-svg-loader!../../assets/icons/exclamation-triangle-solid.svg";
我用 vue-cli
创建了一个 Vue 项目,稍后通过 npm install --save-dev storybook
安装了 storybook
作为依赖项来显示我创建的组件。
故事书 webpack 配置如下所示:
const path = require('path')
module.exports = async ({ config, mode }) => {
config.module.rules.push(
{
test: /\.svg$/,
use: ['vue-svg-loader']
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
sourceMap: false,
data: '@import "./src/assets/styles/main.scss";'
}
}
],
include: path.resolve(__dirname, '../')
}
)
return config
}
故事 index.js
是这样的:
import Vue from 'vue';
import { storiesOf } from '@storybook/vue';
import Alert from '../src/components/buttons/Alert.vue';
storiesOf('Components', module)
.add('Alert', () => ({
components: { Alert },
template: '<Alert />'
}))
出于某种原因,当我尝试加载由 SVG 组成的组件时,我得到:
显示了组件本身,但没有显示 SVG 所在的部分。
有趣的是,尽管它在尝试在 Vue 的主应用程序中显示组件时工作得很好。 vue.config
看起来像这样:
module.exports = {
css: {
loaderOptions: {
sass: {
data: `@import "./src/assets/styles/main.scss";`
}
}
},
chainWebpack: config => {
const svgRule = config.module.rule("svg");
svgRule.uses.clear();
svgRule.use("vue-svg-loader").loader("vue-svg-loader");
}
};
为什么 storybook
无法正确加载 svg 而主 Vue 应用程序可以?
编辑: 我已经继续并简单地使用了 webpacks file-loader
以确保它与 vue-svg-loader
:
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {},
},
],
},
但我遇到了同样的错误。
在尝试应用第一个答案将 push()
替换为 unshift()
规则后,我收到此错误:Error in parsing SVG: Non-whitespace before first tag
.
编辑2:
这是我要加载的 SVG 文件之一:
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="exclamation-triangle" class="svg-inline--fa fa-exclamation-triangle fa-w-18" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"></path></svg>
Edit3: *Alert.vue in projectroot/src/components/banners/Alert.vue
, Storybook 的 Webpack 配置在 projectroot/.storybook/webpack.config.js
<template>
<div v-if="show" :class="`alert_wrap ${model}`">
<IconBase>
<component :is="iconComponent" />
</IconBase>
<div class="alert_text">
<p v-if="title">{{ title }}</p>
<p>{{ msg }}</p>
</div>
<IconBase>
<TimesIcon @click="show = !show" aria-hidden="Close" />
</IconBase>
</div>
</template>
<script>
import IconBase from "../icons/IconBase.vue";
import CautionIcon from "../../assets/icons/exclamation-triangle-solid.svg";
import InfoIcon from "../../assets/icons/info-circle-solid.svg";
import SuccessIcon from "../../assets/icons/check-circle-solid.svg";
import TimesIcon from "../../assets/icons/times-solid.svg";
export default {
name: "Alert",
components: {
CautionIcon,
InfoIcon,
SuccessIcon,
TimesIcon,
IconBase
},
props: {
msg: {
type: String,
required: true
},
title: {
type: String
},
model: {
type: String,
default: "info",
validator(type) {
return ["caution", "info", "success"].includes(type);
}
}
},
computed: {
iconComponent() {
return `${this.model}-icon`;
}
},
data() {
return {
show: true
};
}
};
</script>
<style scoped lang="scss">
/* some styles depending on the props passed */
</style>
因为 storybook
已经有一个 file-loader
可以将您的 svg
作为资产发出。我们应该首先从 file-loader
中排除 svg
。
// exclude svg from existing rule
config.module.rules = config.module.rules.map(rule => {
if (rule.loader && rule.loader.endsWith('file-loader')) {
return {
...rule,
test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/,
};
}
return rule;
});
// now we can add our svg loader
config.module.rules.push(
{
test: /\.svg$/,
use: ['vue-svg-loader']
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
sourceMap: false,
data: '@import "./src/assets/styles/main.scss";'
}
}
],
include: path.resolve(__dirname, '../')
}
)
您使用的是 UTF-8 编码吗?
你有
的错误Error in parsing SVG: Non-whitespace before first tag
用于与使用 Byte-Order-Mark (BOM) 的编码相关。
"Zero width no-break space" unicode character which Windows systems automatically prepend to utf-8 files
如果您使用十六进制编辑器检查文件,您可以看到罪魁祸首。
您可以在加载文件之前手动将编码更改为 "without BOM" 或使用 replace("\ufeff", "") 之类的内容进行替换。
如果您使用的是编辑器,那么您可能具有特定 "with BOM" 编码的配置。例如在记事本++
添加 jsx 选项的代码
loader: 'file-loader',
options: {
jsx: true,
},
@liximomo 的回答几乎是正确的。您应该从 file-loader
处理规则中删除 svg
文件,但规则搜索条件应该略有不同:
module.exports = ({ config }) => {
let rule = config.module.rules.find(r =>
// it can be another rule with file loader
// we should get only svg related
r.test && r.test.toString().includes('svg') &&
// file-loader might be resolved to js file path so "endsWith" is not reliable enough
r.loader && r.loader.includes('file-loader')
);
rule.test = /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/;
config.module.rules.push(
{
test: /\.svg$/,
use: ['vue-svg-loader']
}
)
// ...
return config;
}
另一种在不改变 Webpack 配置规则的情况下导入 SVG 的方法是指定一个 内联 加载器,当某些组件已经在 [=20] 中使用 SVG 时,这有助于避免错误=] 或 HTML 作为文件路径。示例:
// disable all rules with !! and use just vue-svg-loader
import CautionIcon from "!!vue-svg-loader!../../assets/icons/exclamation-triangle-solid.svg";