【油猴脚本】在 Iconfont 上直接复制 React component 代码

sxkk20081年前知识分享68

本文接上一篇《如何在项目中管理你的图标?》

Iconfont 和 SVG 优缺点对比

在上文中介绍了使用 iconfont 的缺点,以及使用 SVG 的优点,简单归纳为以下几点:

Icon 的缺点

  • 当网络不好的时候,会显示方块
  • 如只使用一个图标,字体冗余
  • 维护依赖 iconfont 平台
  • 在组件开发的时候命名冲突

使用 SVG 的优点

  • 完全离线化使用,不需要从 CDN 下载字体文件,图标不会因为网络问题呈现方块,也无需字体文件本地部署。
  • 在低端设备上 SVG 有更好的清晰度。
  • 支持多色图标。
  • SVG 可以支持动画

并给出了最终方案,放弃使用字体,使用 SVG 代替 iconfont。

又给出了实践步骤:

  • 老项目中的 iconfont, 可以通过 nodejs 脚本将下载的 iconfont.svg 转为多个 SVG 图标
  • 新加的图标,可以直接在 iconfont.cn 上下载 SVG
  • React 项目中,如果要直接使用 SVG,需要配置 webpack loader —— @svgr/webpack

下面是 webpack.config.js 中要加入的配置

module.exports = {
  module: {
    rules: [
      {
        test: /\.SVG$/i,
        type: 'asset',
        resourceQuery: /url/, // *.SVG?url
      },
      {
        test: /\.SVG$/i,
        issuer: /\.[jt]sx?$/,
        resourceQuery: { not: [/url/] }, // exclude react component if *.SVG?url
        use: ['@SVGr/webpack'],
      },
    ],
  },
}

上面这段配置看上去很简单,当我往项目中配置时,却又遇到了困难,有的时候打包配置是在一个单独的包中,比如使用 vite 脚手架创建的 react 项目, 想要在项目中支持直接使用 SVG, 就必须写一个自定义 plugin。

所以我写了一个油猴脚本,可以在 iconfont.cn 上直接复制 React component 代码,如此一来,我们就省去了配置 webpack 的烦恼。

使用

Tampermonkey 是一个 chrome 插件,允许开发者直接在上面发布脚本,相当于是一个简易的 chrome 插件,若要在 chrome 扩展商店中发布插件的话,需要花费 5 美元。

名字来源 svgr ,就是 iconfont + React component = IconfontR

iconfontr 效果

装完插件后会在原先的下载按钮边上多出一个复制按钮,点击复制按钮复制 react 代码,就可以在 react 项目中粘贴使用了。

实现原理

其实 svgr 可以提供了在 nodejs 中执行的版本 @svgr/core

安装

npm install --save-dev @svgr/core
# or use yarn
yarn add --dev @svgr/core

引入 @SVGr/core 这个包,我们就可以直接使用啦!

  • source: SVG 源码
  • options: SVGr 配置参数
  • state: 转变为 react component 的配置参数

使用

import { transform } from '@SVGr/core'

const SVGCode = `
  xmlns:xlink="http://www.w3.org/1999/xlink">
      style="stroke:#ff0000; fill: #0000ff"/>

`

const jsCode = await transform(SVGCode, { icon: true }, { componentName: 'MyComponent' })

所以我们可以写一个云函数,直接部署到 vercel 上,下面是 nodejs 云函数代码:

import { VercelRequest, VercelResponse } from '@vercel/node'
import { transform } from '@SVGr/core'

export default async (request: VercelRequest, response: VercelResponse) => {
  const { SVGCode } = request.query
  try {
    const jsCode = await transform(SVGCode, { icon: true }, { componentName: 'SVGComponent' })
    return {
      output: SVGCode,
    }
  } catch (error) {
    response.status(200).send(error.message)
  }
}

当不是成功后,我们就可以直接使用云函数的部署地址,直接通过 fetch 调用就可以啦,传入 SVG 源码,输入 react component 组件源码,当然你也可以使用国内的云开发平台,腾讯云或阿里云,主要是因为 vercel 是完全免费的。

直接使用 svgr playground 的接口

当我看到 svgr playground 的时候,我就想知道它的实现原理,打开控制台一看,我们连云函数都不用写了,它就是一个部署在 vercel 上的一个接口。

access-control-allow-origin: *并且允许跨域,所以我们可以直接调用了。

接下来我们只需要通过 Dom api 获得当前点击元素的 SVG 代码

dom 获取 SVG 元素

在每个图标的操作覆盖层加入一新图标,用于复制 react component

原先是块级布局,一列显示 3 行

为了减少页面空间, 将覆盖的背景层改成 grid 布局,正好 2 行 2 列。

  • grid-template-rows: repeat(2, minmax(0, 1fr)); 平均分 2 行;
  • grid-template-columns: repeat(2, minmax(0, 1fr));平均分 2 列,

脚本全部代码

;(function () {
  // 请求接口
  async function fetchSVGr(code) {
    return await fetch('https://api.react-SVGr.com/api/SVGr', {
      headers: {
        'content-type': 'application/json',
      },
      body: JSON.stringify({
        code: code,
        options: {
          icon: false,
          native: false,
          typescript: false,
          ref: false,
          memo: false,
          titleProp: false,
          expandProps: 'end',
          replaceAttrValues: {},
          SVGProps: {},
          SVGo: true,
          SVGoConfig: {
            plugins: [{ name: 'preset-default', params: { overrides: { removeTitle: false } } }],
          },
          prettier: true,
          prettierConfig: { semi: false },
        },
      }),
      method: 'POST',
      mode: 'cors',
      credentials: 'omit',
    }).then((res) => res.json())
  }

  // 往 head 中插入覆盖样式
  const style = `.page-manage-project .project-iconlist .block-icon-list li.cover .icon-cover-unfreeze, .page-manage-project .project-iconlist .block-icon-list li:hover .icon-cover-unfreeze,
.block-icon-list li:hover .icon-cover {
  display: grid!important;
}
.page-manage-project .project-iconlist .block-icon-list li .icon-cover {
  grid-template-rows: repeat(3, minmax(0, 1fr));
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.block-icon-list li .icon-cover {
  grid-template-rows: repeat(2, minmax(0, 1fr));
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.block-icon-list li .icon-cover .cover-item{
  width:auto;
}
.page-manage-project .project-iconlist .block-icon-list li .icon-cover .cover-code{
  height: auto;
  line-height: 40px;
}
.block-icon-list li .icon-cover .cover-item-line {
  height: auto;
  line-height: 52.5px;
}`

  const styleEl = document.createElement('style')
  styleEl.textContent = style
  document.head.appendChild(styleEl)

  function addCopybtn() {
    console.log([...document.querySelectorAll('.icon-cover')])
    ;[...document.querySelectorAll('.icon-cover')].forEach((item) => {
      const span = document.createElement('span')
      span.title = '复制 React component'
      span.className = 'cover-item iconfont cover-item-line icon-fuzhidaima'

      span.onclick = async () => {
        const SVG = `${
          item.parentNode.querySelector('SVG').innerHTML
        }`
        console.log('SVG', SVG)
        try {
          const res = await fetchSVGr(SVG)
          navigator.clipboard.writeText(res.output)
          console.log('React component 复制成功!')
        } catch (error) {
          console.log('请求服务出错')
        }
      }
      item.appendChild(span)
    })
  }
  // 监听路
  window.onpopstate = function (event) {
    addCopybtn()
  }

  // 调用 `history.pushState()` 或者 `history.replaceState()` 不会触发 `popstate` 事件,所以是点击时,对比 url 判断
  let href = window.location.href
  document.addEventListener('click', (e) => {
    setTimeout(() => {
      if (window.location.href !== href) {
        addCopybtn()
        href = window.location.href
      }
    }, 500)
  })

  //由于异步加载,需要延迟执行
  setTimeout(() => {
    addCopybtn()
  }, 1000)
})()

以上就是本文全部内容,希望这篇文章对大家有所帮助,也可以参考我往期的文章或者在评论区交流你的想法和心得,欢迎一起探索前端。

相关文章

如何利用区块链赚钱?

如何利用区块链赚钱?

  区块链,作为一种新兴技术,已经在全球范围内引起了极大的关注。人们普遍认为,区块链不仅具有革命性的潜力,还能够为个人和企业带来巨大的收益。本文将探讨如何利用区块链赚钱的一些...

AI边缘盒子:新一代技术的创新助力智能化进程加速发展

AI边缘盒子:新一代技术的创新助力智能化进程加速发展

  近年来,随着人工智能技术的快速发展,各行各业都在积极探索如何利用AI推动智能化进程。而AI边缘盒子作为一项新兴技术,在实际应用中扮演着重要角色。本文将从多个角...

实现一个 Code Pen:(五)白嫖云数据库

前言前面的文章中,我们配置好了编辑器,实现了 css、html、js 的编辑,并且可以在浏览器端编译代码,接下来我们需要实现数据存储的功能。再次提一下我的技术栈主要是 Next.js。我们知道使用 N...

小度LC:颠覆世界的人工智能助手,让世界变可能

小度LC:颠覆世界的人工智能助手,让世界变可能

  随着科技的不断进步,人工智能(AI)已经渐渐进入人们的日常生活,智能语音助手也成为了新时代的热门话题。在众多语音助手中,小度LC凭借其卓越的性能和智能化的服务,成为了最受...

AI技术控:如何应对人工智能的挑战及未来发展趋势

AI技术控:如何应对人工智能的挑战及未来发展趋势

  人工智能(AI)技术近年来快速发展,对各行各业产生了重大影响。然而,随着AI技术的迅猛发展,人们对其未来的发展趋势、产业革命的规模及对劳动力市场的影响仍存在很多疑虑和担忧...

AI绘画与PS绘画:探究两种绘画方式的差异

AI绘画与PS绘画:探究两种绘画方式的差异

  随着人工智能技术的快速发展,越来越多的创意设计工作开始用AI(人工智能)绘画代替PS(Photoshop)绘画。虽然两种方式都可以创建美丽的数字画作,但它们之间还是存在很...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。