【油猴腳本】在 Iconfont 上直接複製 React component 代碼

Iconfont 和 SVG 優缺點對比

在上文中介紹了使用 iconfont 的缺點,以及使用 SVG 的優點,簡單歸納爲以下幾點:

Icon 的缺點

使用 SVG 的優點

並給出了最終方案,放棄使用字體,使用 SVG 代替 iconfont。

又給出了實踐步驟:

下面是 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[1] 腳手架創建的 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[4]。

安裝

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

引入 @SVGr/core 這個包,我們就可以直接使用啦!

使用

import { transform } from '@SVGr/core'

const SVGCode = `
<SVG xmlns="http://www.w3.org/2000/SVG"
  xmlns:xlink="http://www.w3.org/1999/xlink">
  <rect x="10" y="10" height="100" width="100"
   />
</SVG>
`

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

所以我們可以寫一個雲函數,直接部署到 vercel[5] 上,下面是 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 列。

腳本全部代碼

;(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 = `<SVG width="128" height="128" fill="currentColor" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/SVG">${ item.parentNode.querySelector('SVG').innerHTML }</SVG>`
        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)

})()

以上就是本文全部內容,希望這篇文章對大家有所幫助,也可以參考我往期的文章或者在評論區交流你的想法和心得,歡迎一起探索前端。

[1]vite: https://vitejs.dev

[2] 安裝 chrome 油猴擴展: https://chrome.pictureknow.com/extension?id=4d999497b75d4eb6acf4d0db3053f1af

[3] 安裝 iconfontr: https://greasyfork.org/zh-CN/scripts/447288-iconfontr

[4]@svgr/core: https://www.npmjs.com/package/@svgr/core

[5]vercel: https://vercel.com/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/yEsY6eMLhYwG1k24JMbaMw