MoreMoney记账总结 2.0

252 阅读2分钟

Vue@cli引入svg坎坷多多

一、引入svg的简单方法

在iconfont上选好图标,然后生成一个svg.js, 把这个svg.js保存到项目里,然后引用,就可以使用svg了

代码示例:

Icon.vue

<template>
	<svg class="icon" @click="$emit('click', $event)">
		<use :xlink:href="`#i-${name}`"></use>
	</svg>
</template>

<script lang="ts">
  import Vue from 'vue';
  import {Component, Prop} from 'vue-property-decorator';
  import './svg'

  @Component
  export default class Icon2 extends Vue {
    @Prop(String) name: string | undefined;
  }
</script>

上面这种方法虽然简单,但是又有点麻烦,因为当我们需要更新svg图标,就需要重新登录iconfont网站,获取新的svg.js, 然后下载下来,再引入项目,能不能让更新svg简单一些?

答案是可以的。所以我们的目标是:通过webpack来引入svg, 如果有新的svg, 直接把下载好的svg复制到对应的icon文件夹, 然后在任何一个组件里写<Icon name="svgname"></Icon>就可以用这个svg.

接下来将一步步实现这个目标

二、如何用Vue@cli引入svg?

2.1 安装svg loader

我们可以很快地搜索到需要安装svg-sprite-loader, 才能在webpack里使用

但问题是,安装过后,需要配置,但官方文档里的配置是写在webpack.config.js, 而我们的项目是用Vue@cli搭建的,并没有webpack.config.js这个文件,只有vue.config.js

2.2 配置vue.config.js

所以我们需要做的就是把官方文档里 webpack.config.js 关于svg-sprite-loader 的配置 翻译成 vue.config.js 里的配置。于是我就去读了vue的官方文档,发现可以在vue.config.js里用一个chainWebpack来修改webpack的配置

Vue官方给的例子是:

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
        .loader('vue-loader')
        .tap(options => {
          // 修改它的选项...
          return options
        })
  }
}

于是照猫画虎,配置写出来我们需要的vue.config.js

const path = require('path')

module.exports = {
  lintOnSave: false,
  chainWebpack: config => {
    const dir = path.resolve(__dirname, 'src/assets/icons') // 当前目录

    config.module
        .rule('svg-sprite')
        .test(/.svg$/)
        .include.add(dir).end() // 只包含icons目录
        .use('svg-sprite-loader').loader('svg-sprite-loader')
        .options({extract: false}).end() //不需要解析成文件
    config.plugin('svg-sprite').use(require('svg-sprite-loader/plugin'), [{plainSprite: true}])
    config.module.rule('svg').exclude.add(dir) // 其他svg loader排除icons目录


  }

}

这样配置完成之后,我们就可以在项目里引用svg了

<svg>
  <use x:link:href="对应的svg图标的name"></use>  
</svg>

2.3 给svg去色

写MoreMoney记账时,有一个需求是高亮svg图标,即本身灰色的svg图标高亮成黄色或者红色,但在写CSS时,我发现有些svg自己带了颜色,无法修改,怎么办呢?

我去打开svg文件,读了读里面的内容,发现一个属性叫fill, 表示填色。于是进一步在控制台里做实验,发现如果去掉svg里的fill属性,那么这个svg就没有自带颜色了。

但问题是,这么多svg图标,难道我们要一个一个地去删除fill属性?

当然不可能。通过搜索,我发现了一个Loader叫 'svgo-loader', 它可以去除fill标签, 配置如下

config.module
.use('svgo-loader').loader('svgo-loader')  // 
.tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]})).end()

于是现在我们的vue.config.js是:

const path = require('path')

module.exports = {
  lintOnSave: false,
  chainWebpack: config => {
    const dir = path.resolve(__dirname, 'src/assets/icons') // 当前目录

    config.module
        .rule('svg-sprite')
        .test(/.svg$/)
        .include.add(dir).end() // 只包含icons目录
        .use('svg-sprite-loader').loader('svg-sprite-loader')
        .options({extract: false}).end() //不需要解析成文件
        .use('svgo-loader').loader('svgo-loader')
        .tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]})).end()
    config.plugin('svg-sprite').use(require('svg-sprite-loader/plugin'), [{plainSprite: true}])
    config.module.rule('svg').exclude.add(dir) // 其他svg loader排除icons目录
  }
}

这样就可以顺利引入svg且给svg去色

2.4 抽取Icon组件

如果每次引用svg都要写一次:

<svg>
  <use x:link:href="对应的svg图标的name"></use>  
</svg>

实在是有些麻烦,于是我们可以抽取出一个Icon组件,每次<Icon name="svg图标对应的Name"></Icon>这样就好。 Icon组件的实现很简单,就是把本身的代码包裹一层,然后接收一个Name作为参数

Icon组件:

<template>
	<svg class="icon" @click="$emit('click', $event)">
		<use :xlink:href="`#i-${name}`"></use>
	</svg>
</template>

2.5 一次性引入所有svg文件

目前还存在的问题是我们是一个一个地引入svg图标,能不能一次性引入一整个svg文件夹?

可以!用一个ImportAll 方法即可

const importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext);
  try {
    importAll(require.context('../assets/icons', true, /.svg$/));
  } catch (error) {
    console.log(error);
  }

现在我们解决了全部的问题,如果有新的svg, 就把它复制到assets/icons目录下,然后在项目里直接<Icon name="svg图标对应的Name"></Icon>就可以愉快地用它了!

抽取公共组件

在写代码时一直牢记:我与重复不共戴天

在目前写的两个页面里,抽取了一些公共组件:

  • Tag

  • Type

  • Input

  • Button

遇到的问题有:

  1. 需要传的参数不同,
  2. 用props 父传子
  3. 用slot ,

比如Input组件 在money页的placeholder是“写点备注吧”

在editLabel页的placeholder是“限三个汉字”

做法是用prop 传递 placeholder变量

EdiltLabel

<FormItem isNewTag="+" placeholder="限3个汉字或5个英文字母..."
									@update:tagName="onTagNameChange"
				></FormItem>

formItem
<input :value="value"
						 @input="onValueChange($event.target.value)"
						 type="text"
						 :placeholder="placeholder">

slot:

在labels页,Button是新建标签

在editLabel页, Button是保存标签

Button.vue

<button class="button" @click="$emit('click', $event)">
		<slot></slot>
	</button>
label.vue
<Button>新建标签</Button>

editLabel.vue

<Button>保存新标签</Button>
  1. 在不同位置的css不同:用props传一个参数进去,然后用该参数判断是否增加一个class

:class = 'a===1? 'newClass originalClass' : 'originalClass''

css不一样,

所以就传递一个变量进去,通过这个变量判断当前组件用在哪一个页面,然后增加一个class修改css

  1. 点击事件传递

以input和button为例

EditLabel 里有 FormItem 组件, FormItem组件里有Input

EditLabel传递value进去

<FormItem isNewTag="+"
	placeholder="限3个汉字或6个英文字母..."
	:value="tag.name"  
        @update:tagName="onTagNameChange"></FormItem>

FormItem 接受 value 参数

<div :class="isNewTag === '+'? 'notes-wrapper notes-wrapper-no-bg' : 'notes-wrapper '">
		<label class="notes">
			<input :value="value"
				 @input="onValueChange($event.target.value)"
				 type="text"
				 :placeholder="placeholder">
		
		</label>
</div>

 @Prop({default: ''}) value: string | undefined;

监听input事件,如果输入框内容变化,就调用onValueChange函数,把当前的值传给上层组件

 onValueChange(value: string) {
       if (this.isNewTag === '+') {  // 如果在修改标签页
        this.$emit('update:tagName', value);
      } else {  // 如果在money页记账,触发更新value事件
        this.$emit('update:value', value);
      }
    }

这样的话, 实现了由父组件管理用户输入的内容, 子组件里的Input只是作为一个中间站,如果有数据更新,就立刻通知父组件

关于Button

如果直接在Button上设置点击事件会发现无法触发,因为用户点击的是里面的button, 而不是外面包裹的Button组件

解决思路是 当用户点击button时,button就通知Button用户需要做事情了

解决方法是 在button的click事件上触发click事件,通知Button,

Button.vue

<button class="button" @click="$emit('click', $event)">
		<slot></slot>
	</button>
Editabel.vue

	<Button @click="saveTag">保存新标签</Button>

这时,里面的button被点击时,会立刻触发外面Button的点击事件,然后外面Button监听到之后,就执行saveTag保存新标签

代码预览链接:fjliang56.github.io/morney-webs…

继续记录自己第一次做项目碰到的问题和解决方法