【油猴腳本】在 Iconfont 上直接複製 React component 代碼
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[1] 腳手架創建的 react 項目, 想要在項目中支持直接使用 SVG, 就必須寫一個自定義 plugin。
所以我寫了一個油猴腳本,可以在 iconfont.cn 上直接複製 React component 代碼,如此一來,我們就省去了配置 webpack 的煩惱。
使用
Tampermonkey 是一個 chrome 插件,允許開發者直接在上面發佈腳本,相當於是一個簡易的 chrome 插件,若要在 chrome 擴展商店中發佈插件的話,需要花費 5 美元。
-
第一步:安裝 chrome Tampermonkey 擴展 [2]
-
第二步:安裝 IconfontR 插件 [3]
名字來源 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
這個包,我們就可以直接使用啦!
-
source: SVG 源碼
-
options: SVGr 配置參數
-
state: 轉變爲 react component 的配置參數
使用
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
爲了減少頁面空間, 將覆蓋的背景層改成 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 = `<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