reactHooks+TS图片懒加载的二次封装

开发中需要使用到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 />
            &nbsp;
            <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={''} />

九:效果预览

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值