react使用本地svg图片
采用react-create-app命令生成项目。
安装svg插件:
yarn add svg-sprite-loader svgo-loader --dev
svgo-loadert 插件也可以不用。
最开始想采用react-app-rewired插件,安装react-app-rewired,customize-cra插件,config-overrides.js配置如下:
const rewireEslint = require('react-app-rewired-eslint');
module.exports = function override (config, env) {
// do stuff with the webpack config...
config = rewireEslint(config, env);
return config
}
const path = require('path')
function resolve (dir) {
return path.join(__dirname, '.', dir)
}
const {
override,
addWebpackModuleRule,
fixBabelImports,
addLessLoader,
adjustStyleLoaders,
addWebpackAlias
} = require('customize-cra')
module.exports = override(
addWebpackModuleRule(
// {
// test: /\.(png|jpe?g|gif|bpm|webp)(\?.*)?$/,
// exclude: path.resolve(__dirname, '../src/assets/images/svg'), //不处理指定svg的文件
// use: {loader: "url-loader",options: {} }
// },
// 配置.svg
{
test: /\.svg$/,
include: path.resolve(__dirname, '../src/assets/icons'), //全局引入
use: [
{ loader: 'svg-sprite-loader', options: {} },
{ loader: 'svgo-loader', options: {symbolId: "icon-[name]"} },
]
}),
// 针对antd 实现按需打包:根据import来打包 (使用babel-plugin-import)
fixBabelImports('import',{
libraryName:'antd',
libraryDirectory:'es',
style:true,//自动打包相关的样式 默认为 style:'css'
}),
addLessLoader({
lessOptions: {
javascriptEnabled: true,
modifyVars: { '@brand-primary': '#1DA57A' },
// localIdentName: '[local]--[hash:base64:5]' // 自定义 CSS Modules 的 localIdentName
}
}),
adjustStyleLoaders(({ use: [, , postcss] }) => {
const postcssOptions = postcss.options;
postcss.options = { postcssOptions };
}),
// 路径别名
addWebpackAlias({
'@': resolve('src'),
'@components': resolve('src/components'),
'@pages': resolve('src/pages')
})
全局引入SVG的所有图片,代码如下:
- 配置全局加载svg图片
index.js代码如下:
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
如果提示找不require,可在tsconfig.json中加入
"types": [
"node", // 加上此处配置
"webpack-env"
]
或.eslintrc.js配置如下:
"env": {
"node": true,
...
}
在index.tsx中引入(相当于项目主入口文件),不需要其他操作:
import '@/assets/icons'
- 代码实现
import React from "react";
const OsIcon = () => {
return (
<svg className="svg-icon"
aria-hidden="true"
>
<use xlinkHref={'#'+ name}></use>
</svg>
)
}
export default OsIcon
在其他页面使用此组件
import OsIcon from "../../components/OsIcon/OsIcon";
<OsIcon name="logout" />
图片怎么都出不来,配置不方面都没有问题,最后定位到应该默认的webpack配置的问题。
生成webpack
yarn eject
查看生成webpack.config.js配置文件,有如下内容:
{
test: /\.svg$/,
use: [
{
loader: require.resolve('@svgr/webpack'),
options: {
prettier: false,
svgo: false,
svgoConfig: {
plugins: [{ removeViewBox: false }],
},
titleProp: true,
ref: true,
},
},
{
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash].[ext]',
},
},
],
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
},
由于此处已经加载svg配置,所以在config-overrides.js配置怎么都不生效,只能在webpack.config.js中配置,删除上述配置,加入配置如下:
{
test: /\.svg$/,
include: path.resolve(__dirname, '../src/assets/icons'),
use: [
{ loader: 'svg-sprite-loader', options: {} },
// { loader: 'svgo-loader', options: {symbolId: "icon-[name]"} },
]
},
启动,显示正常。
封装组件,添加参数与事件
import React, {useMemo, useEffect } from "react";
interface SvgProps {
name: string; // 图片名称
className?: string; //class 名称
rotate?: number;
size?: string | number;
onclick?: any; // onClick事件
}
/** 自定义SVG图片组件 */
const OsIcon= (props: SvgProps) => {
let {
name,
className,
rotate,
size,
onclick,
} = props;
const setData = () => {
name = "";
className = "";
rotate = 0;
size = 24;
onclick = new Function
}
useEffect(() => { setData(), [] }) // 初始化数据,在渲染的时候初始化一次数据,那么第二个参数必须传空数组
const svgclass = useMemo(() => {
if (className) {
return 'svg-icon '+ className.trim()
}
return "svg-icon"
}, [className])
const svgStyle = useMemo(() => {
return {
transform: 'rotate(' + rotate + 'deg)',
fontSize: size + 'px'
}
}, [rotate])
return (
<div className="b-k flex-display flex-ai-center ">
<svg className={svgclass}
aria-hidden="true"
style={svgStyle}
onClick={ onclick }
>
<use xlinkHref={'#'+ name}></use>
</svg>
</div>
)
}
export default OsIcon
svg-iconless定义如下:
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
组件使用如下:
const TabsView = (props:any)=> {
const popupClick =() => {
alert('我是一个弹窗~')
}
return (
<div className="m-b-10 m-r-10 m-l-10 m-t-10">
<OsIcon name="logout" onclick={ popupClick} />
</div>
)
}