首先安装
| yarn add svg-sprite-loader -D |
把 webpack.config.js 翻译成 Vue.config.js
这个库给的示例代码是 webpack.config.js 但是我们现在在用的是 Vue
官方的代码是这样的
| // webpack >= 2 | |
| { | |
| test: /\.svg$/, | |
| loader: 'svg-sprite-loader', | |
| options: { ... } | |
| } | |
| // webpack >= 2 multiple loaders | |
| { | |
| test: /\.svg$/, | |
| use: [ | |
| { loader: 'svg-sprite-loader', options: { ... } }, | |
| 'svg-transform-loader', | |
| 'svgo-loader' | |
| ] | |
| } |
翻译成 Vue.config.js,用到的 chainWebpack
| 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() | |
| // 下文会解决一个 svg 填充问题,也就是下面被注释掉的代码 | |
| config.plugin('svg-sprite').use(require('svg-sprite-loader/plugin'), [{plainSprite: true}]) | |
| config.module.rule('svg').exclude.add(dir) // 其他 svg loader 排除 icons 目录 | |
| // config.module | |
| // .rule('svg-sprite') | |
| // .test(/\.(svg)(\?.*)?$/) | |
| // .include.add(dir).end() | |
| // .use('svg-sprite-loader-mod').loader('svg-sprite-loader-mod').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-mod/plugin'), [{plainSprite: true}]) | |
| // config.module.rule('svg').exclude.add(dir) | |
| } | |
| } |
其他备注
| // svg 相关组件版本 | |
| "svg-sprite-loader": "^6.0.11", | |
| "svgo-loader": "^2.2.1" |
在 shims-vue.d.ts 中添加
这一步是为了解决 ts 报错
| declare module '*.svg' { | |
| const content: string; | |
| export default content; | |
| } |
接着在 TS 里面引入
| <script lang="ts"> | |
| // 笑死我了,这是菜鸡写法 | |
| import x from '@/assets/icons/Labels.svg' | |
| import y from '@/assets/icons/Money.svg' | |
| console.log(x) | |
| console.log(y) | |
| </script> |
这一步的作用就是在 html 的 head 部分嵌入一个 symbol ,接着我们在 template 里面用 <use /> 标签就可以使用啦
| <template> | |
| <div class="nav"> | |
| <router-link to="/money"> | |
| <svg> | |
| <use xlink:href="#Money" /> | |
| </svg> | |
| </router-link> | |
| </div> | |
| </template> |
想必看出来了,这样引入真的 很麻烦,如果我们有几十个 svg ,难道要一个一个的引入??
而且每次都要写 <svg><use /></svg> 好麻烦,我们可不可以把它封装成一个组件呢?
引入整个 svg 目录?
| let importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext); | |
| // 这玩意是搜的全网搜到的? | |
| try {importAll(require.context('../assets/icons', true, /\.svg$/));} catch (error) {console.log(error);} | |
| // 如果不加 try,在单元测试的时候可能会遇到问题 | |
| // 牛逼,好用 |
将 icon 封装成组件
@/components/Icon.vue
| <template> | |
| <svg class="icon"> | |
| <use :xlink:href="'#' + name" /> | |
| </svg> | |
| </template> | |
| <script lang="ts"> | |
| let importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext); | |
| // 这玩意是搜的全网搜到的? | |
| try {importAll(require.context('../assets/icons', true, /\.svg$/));} catch (error) {console.log(error);} | |
| // 如果不加 try,在单元测试的时候可能会遇到问题 | |
| export default { | |
| name: 'Icon', | |
| props: ['name'] | |
| // 以下代码是自动生成的 | |
| // props: { | |
| // name: { | |
| // type: String, | |
| // required: true | |
| // } | |
| // } | |
| } | |
| </script> | |
| <style lang="scss" scoped> | |
| // 这个样式代码是阿里矢量字体库里面给的css | |
| .icon { | |
| width: 1em; | |
| height: 1em; | |
| vertical-align: -0.15em; | |
| fill: currentColor; | |
| overflow: hidden; | |
| } | |
| </style> |
@/main.ts 全局注册
| import Icon from "@/components/Icon.vue"; | |
| Vue.component("Icon", Icon) |
然后直接在 view 里面使用 <Icon />即可
| <template> | |
| <div class="nav"> | |
| <router-link to="/money"> | |
| <Icon name="Money"/> | |
| </router-link> | |
| | | |
| <router-link to="/lables"> | |
| <Icon name="Labels"/> | |
| </router-link> | |
| | | |
| <router-link to="/statistics"> | |
| <Icon name="Statistics"/> | |
| </router-link> | |
| </div> | |
| </template> |
遇到的一个小 bug: fill 颜色
尝试实现切换标签页的时候自动更改填充颜色来达到突出显示的效果
但是并不是所有的 svg 都会自动变色, 只有一些标签会起作用… 于是我直接干脆调成了这样

和这样

笑死我了,直接逃避这个问题
经过排查发现是 svg 代码里面的 fill 属性来控制了颜色,从而 css 不能从外部更改颜色
如图:

手动删掉此属性即可正常用css指定颜色,但是如果有很多svg.这样的话未必太过麻烦
我们可以使用一个叫做 svgo-loader 的插件来解决此问题
不知道是什么原因,高版本的这个插件会导致加载不出 svg 的 bug,我使用的是以下版本,是正常使用的
| // 和上文提到的一样 | |
| { | |
| "devDependencies": { | |
| "svg-sprite-loader": "^6.0.11", | |
| //已知 svg-sprite-loader 的 4.1.6 版本会有 bug | |
| // 故推荐使用高版本的,最新版已经修复了 bug,推荐使用 ^6.0.11 | |
| "svgo-loader": "^2.2.1", // 即此版本可正常使用 | |
| } | |
| } |
我的完整的版本号(可以正常使用的)
| { | |
| "name": "morney-3", | |
| "version": "0.1.0", | |
| "private": true, | |
| "scripts": { | |
| "serve": "vue-cli-service serve", | |
| "build": "vue-cli-service build", | |
| "test:unit": "vue-cli-service test:unit", | |
| "lint": "vue-cli-service lint" | |
| }, | |
| "dependencies": { | |
| "core-js": "3.15.2", | |
| "register-service-worker": "1.7.2", | |
| "vue": "2.6.14", | |
| "vue-class-component": "7.2.6", | |
| "vue-property-decorator": "9.1.2", | |
| "vue-router": "3.5.2", | |
| "vuex": "3.6.2" | |
| }, | |
| "devDependencies": { | |
| "@types/jest": "24.0.25", | |
| "@typescript-eslint/eslint-plugin": "4.28.4", | |
| "@typescript-eslint/parser": "4.28.4", | |
| "@vue/cli-plugin-babel": "4.5.13", | |
| "@vue/cli-plugin-eslint": "4.5.13", | |
| "@vue/cli-plugin-pwa": "4.5.13", | |
| "@vue/cli-plugin-router": "4.5.13", | |
| "@vue/cli-plugin-typescript": "4.5.13", | |
| "@vue/cli-plugin-unit-jest": "4.5.13", | |
| "@vue/cli-plugin-vuex": "4.5.13", | |
| "@vue/cli-service": "4.5.13", | |
| "@vue/eslint-config-typescript": "7.0.0", | |
| "@vue/test-utils": "1.2.2", | |
| "eslint": "6.8.0", | |
| "eslint-plugin-vue": "6.2.2", | |
| "sass": "1.36.0", | |
| "sass-loader": "8.0.2", | |
| "svg-sprite-loader": "^6.0.11", | |
| "svgo-loader": "^2.2.1", | |
| "typescript": "4.1.6", | |
| "vue-template-compiler": "2.6.14" | |
| } | |
| } |
安装完之后,我们需要配置 vue.config.js 文件
| + .use('svgo-loader').loader('svgo-loader') | |
| + .tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]})) | |
| + .end() |
完整的 vue.config.js
| module.exports = { | |
| lintOnSave: false | |
| } | |
| 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 目录 | |
| } | |
| } |
收工,效果如下

PS: vue.config.js 报 eslint 错误怎么办?

| /* eslint-disable */ |
把这句话添加到 vue.config.js 的第一行即可
资料来源:饥人谷。任何组织或个人未经许可,禁止转载
评论




