故事书未在Vue项目的组件中加载SVG

时间:2019-07-10 13:12:54

标签: vue.js webpack vuejs2 vue-component storybook

我已经使用vue-cli创建了一个Vue项目,并稍后通过storybook安装了npm install --save-dev 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的组件时,我得到了:

enter image description here

显示了组件本身,但是未显示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无法在主Vue应用程序可以正常加载的同时正确加载svg?

编辑:我已经继续使用了file-loader的webpack,以确保与vue-svg-loader无关:

{
  test: /\.(png|jpg|gif|svg)$/,
  use: [
    {
      loader: 'file-loader',
      options: {},
    },
  ],
},

但是我遇到了同样的错误。 尝试应用第一个答案将push()替换为unshift()的规则后,出现此错误:Error in parsing SVG: Non-whitespace before first tag

Edit2

这是我要加载的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 :* projectroot/src/components/banners/Alert.vue中的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>

3 个答案:

答案 0 :(得分:2)

@liximomo的答案几乎是正确的。您应该从svg处理规则中删除file-loader文件,但是规则搜索条件应该稍有不同:

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的另一种方法是指定内联加载程序,当某些组件已经在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";

答案 1 :(得分:1)

因为storybook已经有一个file-loader可以将您的svg作为资产散发。我们应该首先从svg中排除file-loader

// 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, '../')
    }
  )

答案 2 :(得分:0)

您是否使用UTF-8编码?

您遇到

错误
  

解析SVG时出错:第一个标记之前为非空白

过去与使用字节顺序标记(BOM)进行编码有关。

  Windows系统的

“零宽度不间断空格” unicode字符   自动添加到utf-8文件中

如果您使用十六进制编辑器检查文件,您会发现罪魁祸首。

您可以在加载文件之前手动将编码更改为“不带BOM”,或用replace(“ \ ufeff”,“”)之类的文件进行替换。

如果使用编辑器,则可能已配置了特定的“带有BOM”编码。例如在记事本++中 N++ encoding

添加jsx选项的代码

 loader: 'file-loader',
      options: {
        jsx: true,
      },