记录 react 在工作中遇到的问题

2024/08/28 react 共 6685 字,约 20 分钟

react中onPaste的时候使用”immer” 更新数据的时候,粘贴内容相同,不会更新子组件渲染

原因: 这是因为 React 默认通过浅比较来判断组件是否需要重新渲染。如果内容没有发生变化,React 将不会更新子组件

解决:强制更新状态,比如使用对象解构的方式来更新内容(setState({…content}) 或者setState({}) 触发更新

react 给路由设置信息,以便后续子路由使用,类似vue的meta,用于后面权限等操作控制

createBrowserRouter([
  {
    path: "*",
    element: <Layout />,
    errorElement: <ErrorPage />,
    loader: () => ({
      name: "layout",
      route: "*",
      parentId: "0",
      id: "0",
      imgUrl: "",
      role: "0",
    })
  }
])

在目标组件中,使用 useMatches 钩子来访问路由的元信息, 路由有几层,useMatches() 的数组长度就有多长(即数组的长度是多少,取决于我的路由有几层嵌套,祖辈=>自己的路由信息)

侧边菜单栏高亮设置(andt的menu组件)

 useEffect(() => {
    const data = matches[1]?.data || matches[0]?.data;
    if (data) {
      setSelectKey([(data as { parentId: string; id: string }).id]);
      setOpenKeys([(data as { parentId: string; id: string }).parentId]);
    }
  }, [location.pathname]);

无权限直接输入路由不让访问

<Route
  key={index}
  path={item.path}
  element={
    <RoleGuard role={item.loader().type}>
      {item.element}
    </RoleGuard>
  }
></Route>

import { type PropsWithChildren } from "react";
import WorkRoleGuard from "./WorkRole";
import { VIDEO_TYPE } from "../../../constant/videoType";

const RoleGuard = ({
  children,
  type,
  role,
}: PropsWithChildren<{
  role?: number | VIDEO_TYPE;
  type?: string;
  elseRender?: React.ReactNode;
}>) => {
  /** 作品列表的权限 */
  if (type === "work" && role) {
    return <WorkRoleGuard role={role}>{children}</WorkRoleGuard>;
  }
  /** 其他权限 */
  return children;
};
export default RoleGuard;

https://imgservices-1252317822.image.myqcloud.com/coco/s08292024/87bada35.3cpb65.jpg

https://imgservices-1252317822.image.myqcloud.com/coco/s08292024/881a5607.oz3poh.jpg

关于权限控制的可以参考 node 里面的一些东西,比如守卫,中间间,路由元信息等,要把学到的东西

pnpm –dir ./packages/vant dev 什么意思

pnpm:这是一个快速、节省磁盘空间的 Node.js 包管理器,与 npm 和 Yarn 相似。

–dir ./packages/vant:这个选项指定了要运行的目录,也就是在 ./packages/vant 路径下。这样做通常在 monorepo 或多包项目结构中,方便在特定子项目中运行命令。

dev:这是在该目录下执行的命令,一般用于启动开发服务器或进行开发环境的构建。在大多数情况下,这个命令会触发定义在 package.json 文件中的相关脚本。

在正则匹配的过程中,想要匹配一个完整的单词,可以使用边界匹配符 \b

text.replace(
    new RegExp(`\\b${record.roleName}\\b`, 'g'),
    `<span style='color:#f37370'>${record.roleName}</span>`
);
\\b 是单词边界的表示可以确保匹配的是完整的单词

ts 获取ts相关类型的方法

ReturnType<typeof setTimeout>

typeof setTimeout: 使用 typeof 操作符来获取 setTimeout 函数的类型这是 TypeScript 的一种机制它允许您获取已有变量或函数的类型

ReturnType<T>: ReturnType  TypeScript 提供的一个内置的条件类型用于提取给定函数类型 T 的返回值类型。(返回值

Parameters<T>: Parameters  TypeScript 提供的另一个内置的条件类型用于提取给定函数类型 T 的参数类型。(参数

T extends (...args: any[]) => any这是一个泛型约束它确保 T 是一个函数类型

typeof这是 TypeScript 的一个操作符用于获取变量或函数的类型

keyof 操作符用于提取对象类型的所有键属性名称),并返回一个由这些键组成的联合类型

keyof typeof xx 是一种常见的类型操作用于提取对象类型的属性名称并将它们组成一个联合类型

let obj = {name: 'Tom', age: 18};
不能直接 keyof obj 是因为obj是一个对象而不是一个类型你需要定义一个类型来表示 obj 的键比如 typeof obj => 这个就是一个类型了

有时候接口返回的数据类型特别复杂,不想挨个挨个写,想偷个懒,可以利用typeof来获取类型,更加方便,比如

export const initSourceBegin = {
  textId: "",
  textContent: "",
  ttsId: 0,
  ttsUrl: "",
  ttsTime: 0,
  ttsDurations: [1],
  videoId: "",
  videoUrl: "",
  duration: 0,
  width: 576,
  height: 1024,
};
export const initData = {
  taskId: "",
  videoBgImg: {
    id: -1,
    imageUrl: "",
    width: 1080,
    height: 1920,
    titleCenterPoint: [540, 240],
    titleSubCenterPoint: [690, 468],
    textBgCenterPoint: [540, 960],
    captionCenterPoint: [540, 1460],
  },
  textBgImg: {
    id: -1,
    imageUrl: "",
    width: 216,
    height: 216,
  },
  templateData: {
    title: {
      data: [
        {
          id: 1,
          imageUrl:
            "https://imgservices-1252317822.image.myqcloud.com/coco/s09272023/07438db3.sdvbu0.png",
          fontSize: 64,
          fontFamily: "HKLJH",
          backgroundColor: "transparent",
          color: "#000",
          strokeWidth: 0,
          strokeColor: "transparent",
          vertical: false,
        },
      ],
    },
    titleSub: {
      data: [
        {
          id: 1,
          imageUrl:
            "https://imgservices-1252317822.image.myqcloud.com/coco/s09272023/07438db3.sdvbu0.png",
          fontSize: 40,
          fontFamily: "HKLJH",
          backgroundColor: "transparent",
          color: "#f00",
          strokeWidth: 0,
          strokeColor: "transparent",
          vertical: false,
        },
      ],
    },
    caption: {
      data: [
        {
          id: 1,
          imageUrl: "/src/assets/fontStyle.png",
          fontSize: 32,
          fontFamily: "HKLJH",
          backgroundColor: "transparent",
          color: "#fff",
          strokeWidth: 6,
          strokeColor: "#000",
        },
      ],
    },
  },
  textLayout: {
    lineNumber: 8,
    fontSize: 30,
    lineHeight: 1.8,
    animationType: 1,
    color: "#FFFFFF",
    textDecorationColor: "#ff0000",
  },
  ttsConfig: {
    ttsType: 5,
    speakSpeed: 1.4,
    volume: 1,
    intonation: 0.95,
  },
  sourceBegin: initSourceBegin,
  activeData: {
    index: -1,
    type: "text",
    indexText: -1,
  },
};
const initType = {
  ...initData,
  audioMaterial: [
    {
      ttsId: 1,
      textContent: "",
      ttsUrl: "",
      ttsTime: 0,
      ttsDurations: [0],
    },
  ],
};
export type DataType = typeof initType;

export type VideoBegin = typeof initData.sourceBegin;

export type TextStyle = typeof initData.textLayout;

简单版react 防抖hooks

import { useState, useEffect } from "react";
const useDebounce = (value: any, delay: number) => {
  const [debouncedValue, setDebouncedValue] = useState(value);
  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);
  return debouncedValue;
};
export default useDebounce;

/** 指定内容修改进行实时保存 */
const debounceUpdate = useDebounce(() => {
  if (mustContentText()) {
    updateVideoDataFetch();
  }
}, 1000);
useEffect(() => {
  debounceUpdate;
}, [data]);

原因: react 内容修改,会导致组件重新渲染,就会触发useEffect 的清理函数

如果使用ts写法

let timer: ReturnType<typeof setTimeout> | null = null;
export function debounce<T extends (...args: unknown[]) => unknown>(
  func: T,
  wait: number,
) {
  return function (...args: Parameters<T>): void {
    timer && clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(null, args);
    }, wait);
  };
}
const debounceUpdate = debounce(updateVideoDataFetch, 1000);
  useEffect(() => {
    if (mustContentText()) {
      debounceUpdate();
    }
  }, [data]);

loash 底层是用requestAnimationFrame实现的

数组循环,可以随机跳步伐,怎么写

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const totalSteps = arr.length;
let currentIndex = 0;

while (currentIndex < totalSteps) {
    console.log(arr[currentIndex]); // 处理当前元素

    // 随机生成一个步长,范围从 1 到 3(可以根据需要调整)
    const step = Math.floor(Math.random() * 3) + 1;
    
    // 更新当前索引,确保不越界
    currentIndex += step;

    // 若越界,跳出循环
    if (currentIndex >= totalSteps) {
        break;
    }
}

sudo vi 和sudo vim 的区别

编辑器: vi 是一个传统的文本编辑器,几乎在所有 Unix 和类 Unix 系统上都可用。它的功能较为基础,适合进行简单的文本编辑。 vim 是 vi 的增强版本(“Vi IMproved”),提供了更多的功能,比如语法高亮、代码折叠、插件支持等。它通常被认为是更强大、更灵活的文本编辑器。

使用体验: 因为 vim 提供了更多的特性和改进,使用 sudo vim 进行编辑文本时,用户可以享受到更好的编辑体验,例如颜色高亮和更丰富的配置选项。 如果系统中没有安装 vim,那么 sudo vi 可以确保你依然可以访问文本编辑功能,因为 vi 是标准工具。 总结来说,选择 sudo vi 还是 sudo vim,通常取决于用户的偏好、可用的工具以及需要的功能。如果你只需要基本的文本编辑功能,而 vim 没有安装,可以用 sudo vi。但如果你更喜欢更强大的功能,且系统中已安装 vim,那么使用 sudo vim 会更好。

有些机器上,你可能没有 vim 的权限,只能使用vi

import “virtual:uno.css”; 里面的virtual 什么意思

virtual: 前缀通常用于 Vite 或者其他支持虚拟模块的构建工具中。这种语法表示你正在导入一个虚拟模块,而不是一个实际存在的文件。

虚拟模块通常是由插件生成的,它们可以在构建时动态生成内容。例如,假设你使用了 UnoCSS(一个实用程序优先的 CSS 框架),它可能会通过 Vite 的插件系统生成相应的 CSS,供你在构建过程中使用。

具体到 import “virtual:uno.css”;:

virtual: 前缀:表示这是一个虚拟模块,不对应任何实际文件,而是由插件或其他工具动态生成的。 uno.css:这个部分表示要导入的模块的名称。在这种情况下,可能是 UnoCSS 插件生成的 CSS 内容。

导入虚拟CSS文件的主要目的是:

动态生成和注入 CSS:根据项目中的实际使用情况生成需要的 CSS 从而减少最终的 CSS 体积。 树摇:只引入实际使用到的样式,优化项目性能。

虚拟模块(Virtual Modules)在 Vite 中是一个强大的功能,允许你动态地生成和使用模块,而不必实际创建这些文件。用途: 动态内容生成/高级插件功能/简化文件管理/代码拆分和优化

@types/xx 是什么意思

@types/xx是TypeScript中用于提供xx库类型定义的包 https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/README.zh-Hans.md

根组件统一处理所有冒泡错误事件

我们创建一个错误边界组件。这个组件将利用 React 的 componentDidCatch 生命周期方法来捕获错误并进行处理,如果我们使用了react-router-dom 的话,可以直接用路由自带的错误处理组件 https://reactrouter.com/en/main/route/error-element#errorelement


在技术的历史长河中,虽然我们素未谋面,却已相识已久,很微妙也很知足。互联网让世界变得更小,你我之间更近。

在逝去的青葱岁月中,虽然我们未曾相遇,却共同经历着一样的情愫。谁的青春不曾迷茫或焦虑亦是无奈,谁不曾年少过

在未来的日子里,让我们共享好的文章,共同学习进步。有不错的文章记得分享给我,我不会写好的文章,所以我只能做一个搬运工

我叫 sunseekers(张敏) ,千千万万个张敏与你同在,18年电子商务专业毕业,毕业后在前端搬砖

如果喜欢我的话,恰巧我也喜欢你的话,让我们手拉手,肩并肩共同前行,相互学习,互相鼓励

文档信息

Search

    Table of Contents