Storybook 不会在 Vue 项目的组件中加载 SVG
Posted
技术标签:
【中文标题】Storybook 不会在 Vue 项目的组件中加载 SVG【英文标题】:Storybook does not load SVGs in Components of Vue project 【发布时间】:2019-11-20 03:05:24 【问题描述】:我用vue-cli
创建了一个Vue 项目,后来通过npm install --save-dev storybook
安装了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 所在的部分未显示 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 在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>
【问题讨论】:
Alert.vue 长什么样子? @Neil.Work 请查看我编辑的答案。 那么如果你注释掉你试图用来渲染图标的动态组件,那么硬编码的TimesIcon渲染成功了吗? @Neil.Work 我不确定,但它在下面得到了解决。 太好了,这就是我想要的。 【参考方案1】:因为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, '../')
)
【讨论】:
感谢您的评论。欣赏它。我已经尝试了您推荐的解决方案,但我收到了一个新错误Error in parsing SVG: Non-whitespace before first tag
。我也继续将其改回file-loader
,这给了我与原始帖子完全相同的错误。
您还应该从文件加载器中排除 svg。【参考方案2】:
您使用的是 UTF-8 编码吗?
你有错误
解析 SVG 时出错:第一个标签前没有空格
用于与使用字节顺序标记 (BOM) 的编码相关。
Windows 系统中的“零宽度不间断空格”unicode 字符 自动添加到 utf-8 文件
如果您使用十六进制编辑器检查文件,您可以看到罪魁祸首。
您可以在加载文件之前手动将编码更改为“无 BOM”或使用 replace("\ufeff", "") 之类的内容进行替换。
如果您使用了编辑器,您可能已经针对特定的“带 BOM”编码进行了配置。例如在记事本++
添加 jsx 选项的代码
loader: 'file-loader',
options:
jsx: true,
,
【讨论】:
感谢您的回答。我已经开始编辑我的 OP 以包含 SVG。我在那里看不到任何without BOM
。顺便说一句,它是 Fontawesomes SVG。
它是内部文件。尽管它是 SVG,但它具有内部编码。可能您使用的工具或创建 SVG 时使用的工具已使用带有 BOM 的 UTF-8 编码,那么现在您正在尝试解析并找到无法解析然后失败的 BOM 字符。没有 BOM 的 UTF-8 也存在。不需要 BOM。 SVG 文件的编码必须是您的接收器可以管理的。编码是地狱。如果您可以更改编码,请尝试使用 Base64 简单的一种。你在用记事本++吗?
我没有使用Notepad++,我实际上根本没有触摸文件。它来自 Fontawesome,所以我刚刚下载了这个文件:https://fontawesome.com/icons/exclamation-triangle?style=solid
并导入了它。
你最终使用的是file-loader还是svg-loader?使用文件加载器并添加选项 jsx: true。我编辑帖子以添加代码。
我已经尝试过vue-svg-loader
和file-loader
,它们都在我的 OP 中给出了错误。使用哪一个并不重要。立即尝试 JSX。【参考方案3】:
@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 的方法是指定一个 inline 加载器,当某些组件已经在 CSS 或 html 中使用 SVG 作为文件路径时,这有助于避免错误.示例:
// disable all rules with !! and use just vue-svg-loader
import CautionIcon from "!!vue-svg-loader!../../assets/icons/exclamation-triangle-solid.svg";
【讨论】:
老兄,非常感谢,终于成功了。你能详细说明问题是什么吗?只是为了让我彻底理解。 @supersize 当然,您的svg
文件是用 file-loader
处理的,它只是为该文件创建 url 并将该文件作为文件路径字符串导入(您在控制台错误中看到了这一点) .所以应该从file-loader
规则test
正则表达式中删除svg
部分,以禁用对此类文件的处理,并允许我们添加自定义vue-svg-loader
加载器。
@supersize 至于解析 SVG 问题,我认为 vue-svg-loader
尝试处理来自 file-loader
的输出 URL 字符串,因为 svg 部分不会从其 test
reg exp 中正确删除。
但是为什么它首先不能只使用file-loader
呢?
@supersize 根据您的代码,我认为您希望将 SVG 作为 Vue 组件加载(您正在导入 svg 并将它们放置到 components
部分)但 file-loader
具有完全不同的目的。跨度>
以上是关于Storybook 不会在 Vue 项目的组件中加载 SVG的主要内容,如果未能解决你的问题,请参考以下文章
在 Vue Storefront Next 项目中使用 @nuxtjs/storybook 时,Vue 组件未呈现 - 可能是 Typescript 问题?
在 storybook v6.4 中加载 css 模块类的问题
写vue3+ jsx+ts语法+ storybook展示的组件库