开发中需要使用到react图片懒加载功能,现在的线上资源关于react图片懒加载的插件并不能达到自己预期的功能
1:图片懒加载(图片元素在页面显示的时候才加载,节省资源,提高运行速度)
2:加载前预览(在图片还没有加载成功/失败的时候,显示加载中的初始图片)
3:加载成功之后显示真正的图片
4:加载失败之后显示错误的提示图片
实现步骤:
一:基于 react-lazyimg-component 库,版本:"react-lazyimg-component": "^1.0.1",基于antd的 Image 组件,实现图片预览
二:封装组件新建 src/components/ImageLazy/index.tsx 文件
以下为整体代码:
import React, { useCallback, useEffect, useState } from "react";
import styles from "./index.module.scss"; //引入样式
import Lazyimg from "react-lazyimg-component"; //引入图片懒加载组件
import { baseURL } from "@/utils/http"; //引入自己定义的服务器/接口初始地址
//加载中占位图片(这里我的路径配置了@根目录,表示src/,没有配置可自行修改)
import imgLoding from "@/assets/img//loading.gif";
import imgErr from "@/assets/img//IMGerror.png"; //加载失败占位图片
import { EyeOutlined } from "@ant-design/icons"; //antd预览的图标
import { useDispatch } from "react-redux"; //redux仓库管理数据(不使用仓库的可以忽略)
type Props = {
width?: number; //图片的宽度
height?: number;//图片的高度
src: string; //图片地址(不用传入服务器/接口初始地址)
noLook?: boolean;//是否开启预览图片(为true则不开启)
};
function ImageLazy({ width = 100, height = 100, src, noLook }: Props) {
const dispatch = useDispatch(); //引入仓库
// 图片占位符
const [placeholderUrl, setPlaceholderUrl] = useState(
src ? imgLoding : imgErr
);
// 默认不能预览图片,加载成功之后能预览
const [lookImg, setLookImg] = useState(false);
useEffect(() => {
if (src) {
// 进页面查看图片的加载情况
// 创建一个img标签
const imgDom = document.createElement("img");
imgDom.src = baseURL + src;
// 不管图片加载成功或者失败,都删除掉,提高性能
// 图片加载成功
imgDom.onload = function () {
setLookImg(true);
imgDom.remove();
};
// 图片加载失败
imgDom.onerror = function () {
setPlaceholderUrl(imgErr);
imgDom.remove();
};
return () => {
// 离开页面也删掉掉元素
imgDom.remove();
};
}
}, [src]);
// 点击预览图片
const lookBigImg = useCallback(() => {
dispatch({
type: "imgLazy/lookBigImg",
payload: { url: baseURL + src, show: true },
});
}, [dispatch, src]);
return (
<div className={styles.ImageLazy} style={{ width: width, height: height }}>
<div className="lazyBox">
<Lazyimg
src={src ? baseURL + src : ""}
width={width}
height={height}
placeholder={placeholderUrl}
alt=""
/>
{/* 图片预览 */}
{noLook || !lookImg ? null : (
<div className="lookImg" onClick={lookBigImg}>
<EyeOutlined />
<div>预览</div>
</div>
)}
</div>
</div>
);
}
const MemoImageLazy = React.memo(ImageLazy); //基于React.memo,避免不必要的刷新
export default MemoImageLazy;
三:样式文件:
.ImageLazy{
position: relative;
:global{
.lazyBox{
width: 100%;
height: 100%;
position: relative;
.lookImg{
cursor: pointer;
transition: opacity .3s;
opacity: 0;
pointer-events: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
color: #fff;
background-color: rgba(0,0,0,.6);
&>div{
font-size: 14px;
}
}
&:hover{
.lookImg{
opacity: 1;
pointer-events: auto;
}
}
}
}
}
四:加载中和加载失败的图片(可自行替换)
加载中:
加载失败:
五:仓库中的操作:
//1.---------新建 src/store/reducer/imgLazy.ts
// 初始化状态应用注解
const initState = {
// 所有图片点击预览查看大图
lookBigImg: {
url:'',
show:false
},
};
type ActionType =
| { type: "imgLazy/lookBigImg"; payload: any } //这里暂时使用any,可自行调整
// reducer
export default function imgLazyReducer(
state = initState,
action: ActionType
) {
switch (action.type) {
// 所有图片点击预览查看大图
case "imgLazy/lookBigImg":
return { ...state, lookBigImg: action.payload };
default:
return state;
}
}
//2.---------新建 src/store/reducer/index.ts(用来合并Reducer)
import { combineReducers } from "redux";
//导入上面的imgLazyReducer
import imgLazyReducer from "./imgLazy";
// 合并 reducer
const rootReducer = combineReducers({
imgLazyStore: imgLazyReducer,
});
export default rootReducer;
//3.---------新建 src/store/index.ts(用来创建和声明仓库并导出)
import { applyMiddleware, legacy_createStore as createStore } from 'redux'
import rootReducer from './reducer'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
// 创建仓库实例
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)))
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
// 导出仓库实例
export default store
六:预览图片的元素dom(建议在App.tsx入口中使用) 这样的好处是只需生成一个dom,就可以实现全局的预览图片
import "@/assets/styles/base.css";
// 关于路由
import React from "react";
import { Router, Route, Switch } from "react-router-dom";
import history from "./utils/history";
import AuthRoute from "./components/AuthRoute";
import SpinLoding from "./components/SpinLoding";
import AsyncSpinLoding from "./components/AsyncSpinLoding";
import { Image } from "antd";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "./store";
const Layout = React.lazy(() => import("./pages/Layout"));
const Login = React.lazy(() => import("./pages/Login"));
export default function App() {
const dispatch = useDispatch();
// 从仓库中获取查看图片的信息
const lookBigImg = useSelector(
(state: RootState) => state.imgLazyStore.lookBigImg
);
return (
<>
{/* -----这一段是我的项目中路由和加载中的组件,可以忽略。上面的导入信息根据情况删除 */}
{/* 关于路由 */}
<Router history={history}>
<React.Suspense fallback={<SpinLoding />}>
<Switch>
<Route path="/login" component={Login} />
<AuthRoute path="/" component={Layout} />
</Switch>
</React.Suspense>
</Router>
{/* 发送请求的加载组件 */}
<AsyncSpinLoding />
{/* -----这一段是我的项目中路由和加载中的组件,可以忽略。上面的导入信息根据情况删除 */}
{/* 所有图片点击预览查看大图 */}
<Image
preview={{
visible: lookBigImg.show,
src: lookBigImg.url,
onVisibleChange: (value) => {
// 清除仓库信息,关闭图片预览
dispatch({
type: "imgLazy/lookBigImg",
payload: { url: "", show: false },
});
},
}}
/>
</>
);
}
七:本人这个项目的package.json
{
"name": "demo",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.3",
"@types/react": "^18.0.24",
"@types/react-dom": "^18.0.8",
"antd": "^5.0.4",
"axios": "^1.1.3",
"dayjs": "^1.11.7",
"echarts": "^5.4.0",
"js-base64": "^3.7.3",
"js-export-excel": "^1.1.4",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-lazyimg-component": "^1.0.1",
"react-redux": "^8.0.4",
"react-router-dom": "5.3",
"react-scripts": "5.0.1",
"redux": "^4.2.0",
"redux-devtools-extension": "^2.13.9",
"redux-thunk": "^2.4.1",
"sass": "^1.55.0",
"typescript": "^4.8.4",
"web-vitals": "^2.1.4"
},
"scripts": {
"dev": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/history": "^5.0.0",
"@types/react-router-dom": "^5.3.3",
"customize-cra": "^1.0.0",
"default-passive-events": "^2.0.0",
"react-app-rewired": "^2.2.1"
},
"homepage": "."
}
八:使用
导入:
import ImageLazy from "@/components/ImageLazy";
{/* src为空就会显示加载失败的占位图片 */}
<ImageLazy width={80} height={50} src={''} />
九:效果预览