图标使用新姿势- react 按需引用 svg 的实现入门指南_图标使用攻略

前言图标是前端在业务开发中不得不写的一个东西,以我司的几个部门为例,每个组在写图标上都有不一样的方式:用户平台:单色图标用 iconfont 上提供的字体文件,彩色图标用 img 引入代替或者使用iconfont 上提供的 symbol.js 。saas:引入 svg 文件,通过 react-svg-loader 将其包裹成一个 react 组件使用。到店购

图标使用新姿势- react 按需引用 svg 的实现入门指南

前言

图标是前端在业务开发中不得不写的一个东西,以我司的几个部门为例,每个组在写图标上都有不一样的方式:

图标使用新姿势- react 按需引用 svg 的实现入门指南_图标使用攻略

  • 用户平台:单色图标用 iconfont 上提供的字体文件,彩色图标用 img 引入代替或者使用iconfont 上提供的 symbol.js 。
  • saas:引入 svg 文件,通过 react-svg-loader 将其包裹成一个 react 组件使用。
  • 到店购:引入 svg 文件,通过 svg-sprite-loader 将所有 svg 图标处理成 svg 雪碧图的方式使用。

这几种使用方式各有千秋,下面谈一谈他们的优缺点:)

用户平台的使用方式【简单】,不需要手动引入每个 svg 文件,缺点是字体图标不如 svg 文件【可扩展性好】,同时为了引入一个图标引入一个完整的字体图标也会带来一定冗余。

而其他两个组的问题在于【图标的引入】以及【管理】方面,需要手动引入 svg 文件,当然优点也非常可观。

这里明确一个事实:svg 图标的综合表现是远大于字体图标的,从 antd 从 3.9.0 的更新就可以看出来。

摘自官方文档

图标使用新姿势- react 按需引用 svg 的实现入门指南_图标使用攻略

antd 的图标使用体验一直很好,比如下面的代码就可以定义一个 home 图标

<Icon type="home" />

不需要事先引入任何资源 ,只需要指定 type = “home” 就可以使用。但是 antd 没有解决一个问题,那就是如何做到图标的按需引用?

摘自官方文档

图标使用新姿势- react 按需引用 svg 的实现入门指南_图标使用攻略

即便是这里提到的 webpack 插件 也不过是图标改成了后置引入,并没有解决图标的按需引用问题。

当然 antd 不好优雅的这个问题是由它的使用方式决定的(合理猜测),作为一个流行的组件库,antd 在引入新的技术的同时又要照顾之前使用者的使用体验,不可避免的会出现一些瑕疵。这是可以理解的,不过换成我们普通业务开发而言,我们没有必要去追求太过完美的开发体验,做出略微的牺牲即可实现【既保持 antd Icon 一样的使用方式,又按需引用了 svg 文件】,怎么实现呢?

如何处理 svg

svg-sprite-loader 是一个在 webpack 中应用比较广泛的 svg 处理库,它可以将代码里引入的 svg 文件合并到一起,然后以 svg symbol 的方式使用,关于它的使用方式网上有大量的文章,所以本文不会再描述它如何使用,请读者自行查阅,

值得一提的是,介绍此 loader 的的文章中,一般都会附带如何一次性引入项目中需要的所有 svg 的方法,那就是利用 webpack 的 require.context api,这个 api 可以获取一个特定的上下文,主要用来实现自动化导入模块,所以为了不再每个模块中一一写 import ‘xxx.svg 这样的语句,使用这个 api 是有必要的。

借助 require.contet 和 svg-sprite-loader 能够使图标开发体验上升一个档次,也能配合 React 组件实现类似 antd Icon 的使用方式。

但是这种使用方式存在一个缺点,那就是【如何避免引入不必要的 svg】,要知道 require.contet 可不会区分哪些 svg 是真正需要的,当然对于个人项目而言,我们可以给一个页面固定一个文件夹存在真正需要的 svg 文件,但是对于多页面的 repo 而言,我们无法也没必要给每一个页面都设置一个专门存放该页面需要的 svg 的文件夹。

作为一个挑剔的程序员,我需要一种更智能更自动化的方式去引入我真正需要的 svg 图标。

思路分析

现在要解决的问题是我需要在写下类似以下代码的时候:

<Icon type="close" />

有种工具能同时在文件中帮我 import 一个 close.svg 。

比如下面的代码:

import Icon from './Icon.jsx';

ReactDOM.render(<Icon type="close"/>);

经过处理后变成这样:

import Icon from './Icon.jsx';
import './assets/close.svg'

ReactDOM.render(<Icon type="close"/>);

想一想,之前使用过什么工具?会自动帮我们引入我们所需要的代码呢?

答案是: babel-plugin-transform-runtime ,一个自动帮前端工程师导入 polyfill 的 babel 插件,

以下是官网介绍

Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals

所以,参考 babel-plugin-transform-runtime 的原理和作用 ,我们想要自动导入一个 svg,也可以借用 babel-plugin 实现。

实现原理

熟悉 babel 的同学,应该知道 babel 插件作用原理,是通过对转化成 ast 的 js 代码做一些更改、替换之类的操作,不熟悉的同学可以点 这里 了解一下 babel 插件是如何开发的。

以前文我们提到的这一句代码 <Icon type=”close”/> 为例,它经过 babel 转化后的 ast 长这个样子

图标使用新姿势- react 按需引用 svg 的实现入门指南_图标使用攻略

转化成 json 会更清晰一些:

{
 "expression": {
    "type": "JSXElement",
    "start": 0,
    "end": 20,
    "openingElement": {
      "type": "JSXOpeningElement",
      "start": 0,
      "end": 20,
      "attributes": [
        {
          "type": "JSXAttribute",
          "start": 6,
          "end": 18,
          "name": {
            "type": "JSXIdentifier",
            "start": 6,
            "end": 10,
            "name": "type"
          },
          "value": {
            "type": "Literal",
            "start": 11,
            "end": 18,
            "value": "close",
            "raw": "\"close\""
          }
        }
      ],
      "name": {
        "type": "JSXIdentifier",
        "start": 1,
        "end": 5,
        "name": "Icon"
      },
      "selfClosing": true
    },
    "closingElement": null,
    "children": []
    }
  }

因为用的是 Jsx 语法,所以这个表达式的 type 是 JSXElement , 同时设置了了 props.type的值为 close , 所以他会有个 name 为 type 而 value 为 close 的 JSXAttribute .

我们在 babel plugin 中可以拿到上述的分析结果,自然也知道了这条语句产生的作用是:

  1. 我写下了一个 type 为 close 的 Icon Component ,
  2. 我希望它能够放一个 close.svg 在这里

所以我们可以 new 一个 Set() 对象,将当前 close 这个关键词存放进去, 为什么用 Set ,因为 Set 中的对象是不想等的,免去重复添加关键词然后再去重的必要。

代码演示:

function plugin({ types: t }) {
  return {
    visitor: {
      Program: {
        enter(path, state) {
          state.svgSet = new Set();
        }
      }
    }
  };
}

在初次访问整个语法树的时候,创建一个 Set 对象,注意 svgSet 一定要挂在 state 上。

然后借用 babel plugin 分析此文件内的所有 JSXElement ,直到整个文件的代码被处理完毕,这样我们就能拿到一个装满了所有关键词的 Set 对象。

代码片段:

function plugin({ types: t }) {
  return {
    visitor: {
      Program: {
       ...
      },
      JSXElement(path, state) {
        const {
          openingElement: {
            attributes
          }
        } = path.node;
        attributes
          .forEach(({ name, value }) => {
            // 判断 name.name 是否等于 "type" 或者是其他设置好的关键词
            state.svgCache.add(value.value);
          });
      }
    }
  };
}

最后,将 Set 里存放的 svg ,遍历之后,用 babel 工具库生成如下的语句:

import 'xxx.svg'

然后插入到此文件的最顶端,剩下的事情就交给 webpack 以及其他 loader 处理了。

我已经将上述代码封装了一个 npm 包 ,欢迎大家下载和体验,当然目前还比较简陋,源码和详细文档也将在不久后发布。

还有 vue 版本的工具也在开发中。

后记

这篇文章实现的 babel 插件原理并不复杂,记录下来希望能够帮助到大家:遇到项目中的问题的时候可以参考社区的实现来解决。最后欢迎大家关注酷家乐前端团队,可以找我私聊或者内推,我的邮箱:titian@qunhemail.com

代码参考:babel-plugin-transform-runtime,babel-plugin-import

工具使用:在线预览 ast

 

海计划公众号
(0)
上一篇 2020/03/29 01:46
下一篇 2020/03/29 01:46

您可能感兴趣的内容

  • strider基础知识教程_一个开源的持续部署/持续集成平台

    strider基础知识教程 官方网址:http://strider-cd.github.io/ GitHub:https://github.com/Strider-CD/strid…

    2020/03/06
  • 如何用vue制作一个探探滑动组件【转】攻略教程_vue使用教程

    前言嗨,说起探探想必各位程序汪都不陌生(毕竟妹子很多),能在上面丝滑的翻牌子,探探的的堆叠滑动组件起到了关键的作用,下面就来看看如何用vue写一个探探的堆叠组件 一. 功能分析简单使用下探探会发现,堆叠滑动的功能很简单,用一张图概括就是:简单归纳下里面包含的基本功能点:图片的堆叠图片第一张的滑动条件成功后的滑出,条件失败后的回弹滑出后下一张图片堆叠到顶部体验

    2020/04/05
  • coda基础指南Panic推出适用在Mac上的网页开发工具

    coda菜鸟指南 官方网址:https://www.panic.com/coda/ 简介描述:Panic推出适用在Mac上的网页开发工具 Coda是Panic推出适用在Mac上的网…

    2020/03/06
  • 底部粘连(stiky footer)布局基础知识教程_布局小白教程

    前面的话在网页设计中,Sticky footers设计是最古老和最常见的效果之一,大多数人都曾经经历过。它可以概括如下:如果页面内容不够长的时候,页脚块粘贴在视窗底部;如果内容足够长时,页脚块会被内容向下推送。本文将详细介绍sticky footer的4种实现方式 绝对定位常见的实现方法是对(.sticky)footer进行绝对定位,假设高度为50px。对父

    2020/03/26
  • 详细介绍 Weex 的 JS Framework【转】使用说明_weex小白指南

    Framework。但是文章写于 2016 年 8 月份,这都是一年半以前的事了,说是“详解”其实解释得并不详细,而且是基于旧版 .we 框架写的,DSL 和底层框架各部分的功能解耦得的并不是很清楚。这一年多以来 JS Framework 已经有了很大的变化,不仅支持了 Vue 和 Rax,原生容器和底层接口也做了大量改造,这里再重新介绍一遍。在 Weex

    2020/04/05
  • Jupyter菜鸟教程_在线交互计算笔记本

    Jupyter菜鸟教程 官方网址:http://jupyter.org/ 简介描述:在线交互计算笔记本 「Jupyter Notebook」是一个以网页的形式打开,可以在网页页面中…

    2020/03/10
  • 如何成为优秀的程序员?入门基础教程_程序员小白帮助

    今天关注了一个很有意思的问题“ 中国目前最优秀的程序员有哪些 ?”。和小时候有人问“你的理想是什么?”,你说想当科学家一样,作为程序员的理想就是成为最优秀的程序员。当然这个问题更加宽泛,生活中有很多事情不是自己想做就能够做到的。你不再是一个单独个体,往小里说,需要养家糊口,往大里说,每个人都生活在时代的洪流中。有时候回顾过去,我往往会产生一些不切实际的想法。

    2020/03/20
  • 逐点分析,这样做Web端性能测试入门教程_测试零基础入门

    前言:71%用户希望在手机上打开网页能跟电脑一样快;5秒钟被认为是用户能忍受的最长响应时间,如果响应时间超过5秒,50%的移动用户会放弃;33%失望的用户会使用竞品替代;用户尝试三次出现同样性能问题,50%的人不会再使用该应用。基于此,我们今天就一起来探讨一下Web性能测试测试点。(写的略粗糙,欢迎大家留言吐槽。)1、什么是Web性能测试?注意事项有哪些?性

    2020/03/26
  • 用Node.js编写内存效率高的应用程序小白入门_效率基础知识

    软件应用程序在计算机的主存储器中运行,我们称之为随机存取存储器(RAM)。JavaScript,尤其是 NodeJS (服务端 JS)允许我们为终端用户编写从小型到大型的软件项目。处理程序的内存总是一个棘手的问题,因为糟糕的实现可能会阻塞在给定服务器或系统上运行的所有其他应用程序。C 和 C++ 程序员确实关心内存管理,因为隐藏在代码的每个角落都有可能出现可

    2020/03/20
  • 跨域解决方案之JSONP基础知识教程_jsonp使用指南

    同源策略同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源

    2020/03/26
  • css图标库菜鸟攻略css常用的矢量图标大全_图标基础入门

    以前网页上显示图标都是用切成小图片,然后根据不同场景作多个小图,通过JS来控制显示效果。 字图图标,顾名思义,就像使用通常的字体,可以设置字体颜色,大小等,不用在搞多张小图片,非常灵活。其优点:轻松的定义图标的颜色,大小,阴影,和任何与CSS相关的特性。更快的载入速度、样式更容易定义。使用矢量字体,这意味着他们可以完美的显示在高分辨率显示器中。Iconfon

    2020/04/03
  • 前端JavaScript设计模式入门指南_模式菜鸟攻略

    一、基础知识1. 面向对象的JavaScript面向对象的三大特性:继承、封装、多态。JavaScript 没有提供传统面向对象语言中的类式继承,而是通过原型委托的方式来实现对象与对象之间的继承。JavaScript 也没有在语言层面提供对抽象类和接口的支持。正因为存在这些跟传统面向对象语言不一致的地方,我们在用设计模式编写代码的时候,更要跟传统面向对象语言

    2020/03/20
  • Laravel异常:捕获,处理和创建菜鸟教程下载_异常使用教程

    很多开发者在开发过程中都会遇到异常,处理过程大同小异:捕获然后处理,事实上也确实是如此。但本文不打算谈太多错误与异常的原理,只是从laravel自带的Exception入手,谈一谈怎样用一个更好的方式处理错误信息。异常先举个简单的例子,在laravel中,如果一个Model找不到或者没有,很容易就抛出一个异常,大家常见的Whoops, something w

    2020/03/30
  • ui-router小白基础_一个AngularJS路由模块

    ui-router小白基础 官方网址:http://ui-router.github.io/ GitHub:https://github.com/angular-ui/ui-rou…

    2020/03/06
  • 使用TypeScript两年后-值得吗?小白攻略_TypeScript指南教程

    差不多两年前,我在一个创业团队中开始了一个全新的项目。用到的全都是类似Microservices,docker,react,redux这些时髦的东西。我在前端技术方面积累了一些类似的经验,因为在更早的一年前我带着20多名前端开发人员编写了一个非常大的react应用程序。这对我来说非常具有挑战性。当时我们遇到了很多问题:模型内聚的问题,代码库的增长,复杂且难以

    2020/03/30
  • js中的位运算及用法菜鸟教程网_运算使用说明

    什么是位运算?位运算是在数字底层(即表示数字的 32 个数位)进行运算的。由于位运算是低级的运算操作,所以速度往往也是最快的(相对其它运算如加减乘除来说),并且借助位运算有时我们还能实现更简单的程序逻辑,缺点是很不直观,许多场合不能够使用。位运算只对整数起作用,如果一个运算子不是整数,会自动转为整数后再运行。虽然在 JavaScript 内部,数值都是以64

    2020/03/29