React项目——即时通讯


安装Styled-components

npm install styled-components

Styled-components的一大好处就是可以使用主题,可以将主题的背景颜色,字体大小设置在主题中:

theme.js

export default {
    primaryColor:"#4F9DDE"
}

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import {ThemeProvider} from "styled-components"
import theme from './theme.js'


ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <ThemeProvider theme={theme}></ThemeProvider>
    <App />
  </React.StrictMode>,
)

安装StoryBook

Storybook 是一个开源工具,用于构建 UI 组件并创建组件库。它提供了一个沙盒环境,开发者可以在其中独立地开发和测试 UI 组件,无需担心应用程序的其他部分。

npx -p @storybook/cli sb init

注意:storybook需要node.js版本在18及以上。

可以使用nvm管理node版本:

  • 在终端中运行以下命令来安装最新版本的 Node.js:

    nvm install node
  • 然后,你可以使用以下命令来切换到新安装的版本:

    nvm use node

启动storybook:

npm run storybook

配置主题样式到storybook中:

创建一个全局装饰器,这个装饰器是一个函数,接收一个组件 Story 作为参数,返回一个 ThemeProvider 组件。

Story 组件的渲染结果会作为 ThemeProvider 的子组件,这样所有的故事都会被 ThemeProvider 包裹,从而能访问到主题样式。

CHAT-UI\.storybook\preview.jsx

import React from 'react';
import { ThemeProvider } from 'styled-components';
import theme from '../src/theme';

export const decorators = [(Story) => <ThemeProvider theme={theme}><Story /></ThemeProvider>];

此项目设计稿原作者:https://dribbble.com/shots/5262706-Daily-UI-13-Direct-Messaging

头像组件

image-20240519170401436

共有四种头像类型,需要动态设计的地方:

  • 头像图片
  • 有无在线状态
  • 在线状态的颜色
  • 头像图片的尺寸

注意:组件文件夹下一般包括以下几个文件:

  • index.js —— 组件主体代码
  • style.js —— Styled-components 样式组件代码
  • [组件名].stories.js —— 组件 stories
  • hook.js —— 自定义的hooks

index.jsx

import React from "react";
import PropTypes from "prop-types";

import { StatusIcon, StyledAvatar, AvatarClip, AvatarImage } from "./style";
// 定义 Avatar 组件。它接收几个 props:src(头像的图片地址),size(头像的尺寸),status(用户的在线状态),statusIconSize(状态图标的尺寸),以及其他的 props(...rest)。
function Avatar({
    src,
    size = "48px",
    status,
    statusIconSize = "8px",
    ...rest
  }) {
    // 返回一个 JSX 元素。这个元素是一个 StyledAvatar 组件,它接收所有的 rest props。
    return (
      <StyledAvatar {...rest}>
        {/* 如果 status prop 存在,就渲染一个 StatusIcon 组件。这个组件接收 status 和 size props。 */}
        {status && (
          <StatusIcon $status={status} size={statusIconSize}></StatusIcon>
        )}
  
        {/* 渲染一个 AvatarClip 组件,它接收 size prop。AvatarClip 组件的子元素是一个 AvatarImage 组件,它接收 src 和 alt props。 */}
        <AvatarClip size={size}>
          <AvatarImage src={src} alt="avatar" />
        </AvatarClip>
      </StyledAvatar>
    );
  }
  
  // 使用 PropTypes 对 Avatar 组件的 props 进行类型检查。src 是必需的,size 和 statusIconSize 是可选的,status 必须是 "online" 或 "offline"。
  Avatar.propTypes = {
    src: PropTypes.string.isRequired,
    size: PropTypes.string,
    status: PropTypes.oneOf(["online", "offline"]),
    statusIconSize: PropTypes.string,
  };
  
  // 导出 Avatar 组件,使得其他文件可以导入和使用它。
  export default Avatar;
重点 描述
PropTypes 使用 PropTypes 对 props 进行类型检查,可以帮助开发者理解组件期望的输入,并且在开发过程中提供警告。
默认参数 在函数参数中使用默认值,可以使得函数在没有提供某些参数的情况下仍然可以正常工作。
解构赋值 使用解构赋值来获取 props,可以使得代码更加清晰和简洁。
条件渲染 使用条件渲染来决定是否渲染 StatusIcon,可以使得组件更加灵活。
rest 参数 使用 rest 参数来获取所有未被单独获取的 props,然后将它们传递给 StyledAvatar。这是一个好的习惯,可以使得 Avatar 组件可以接收并使用任意数量的 props。
需要注意的点 描述
未知的 props 当你将一个未知的 prop(如 "status")传递给 styled-components 时,它会被传递给 DOM,从而触发一个警告。你可以使用 transient props(如 "$status")来避免这个警告。
有效的 HTML 属性 并非所有的 props 都是有效的 HTML 属性。当你将一个无效的 prop(如 "status")传递给 DOM 元素时,React 会在控制台中打印出警告。你需要确保只有有效的 HTML 属性被传递给 DOM 元素。
PropTypes 的限制 PropTypes 只能在开发模式下进行类型检查。在生产模式下,为了提高性能,PropTypes 会被自动忽略。因此,你不能依赖 PropTypes 来阻止错误的 props 被传递给组件。

在React中,当你向一个组件传递props时,这些props会被传递到组件的DOM元素上。然而,并非所有的props都是有效的DOM属性。

例如,"status"就不是一个有效的DOM属性。当无效的props被传递到DOM元素上时,React会在控制台中打印出警告。

style.js

import styled, { css } from "styled-components";


// circleMixinFunc 是一个函数,它接收颜色和尺寸作为参数,并返回一个 css 模板字符串。这个字符串定义了一个绝对定位的块级元素,它的宽度、高度和背景颜色都由参数指定,形状为圆形。
const circleMixinFunc = (color, size = "8px") => css`
  content: "";
  display: block;
  position: absolute;
  width: ${size};
  height: ${size};
  border-radius: 50%;
  background-color: ${color};
`;

const StyledAvatar = styled.div`
  width: 48px;
  height: 48px;
  position: relative;
`;
const StatusIcon = styled.div`
  position: absolute;
  left: 2px;
  top: 4px;

  // ::before 伪元素的样式由 circleMixinFunc 生成,颜色被设置为白色,尺寸由 size prop 指定,然后被放大两倍。
  &::before {
   ${({size}) => circleMixinFunc("white", size)}
   
    transform: scale(2);
  }
  // ::after 伪元素的样式也由 circleMixinFunc 生成,颜色和尺寸由 props 指定。如果 status prop 是 "online",颜色就被设置为绿色;如果 status prop 是 "offline",颜色就被设置为灰色。
  &::after {
    ${({ theme, $status, size }) => { // 此处的size是statusIconSize,并非AvatarClip的size
      if ($status === "online") {
        return circleMixinFunc(theme.green, size);
      } else if ($status === "offline") {
        return circleMixinFunc(theme.gray, size);
      }
    }}
  }
`;

const AvatarClip = styled.div`
  width: ${({ size }) => size};
  height: ${({ size }) => size};
  border-radius: 50%;
  overflow: hidden;
`;
const AvatarImage = styled.img`
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
`;

export { StyledAvatar, StatusIcon, AvatarClip, AvatarImage };
重点 描述
样式封装 使用 circleMixinFunc 封装了圆形元素的公共样式,这是一个好的习惯,可以使得代码更加清晰和简洁。
伪元素 使用 ::before::after 伪元素来创建额外的视觉效果。
动态样式 使用函数来动态生成样式,这是一个好的习惯,可以使得你的组件更加灵活。
主题 使用 theme prop 来获取主题颜色,这是一个好的习惯,可以使得你的组件更加易于定制和维护。
需要注意的点 描述
伪元素的内容 ::before::after 伪元素需要 content 属性,否则它们不会被显示。
伪元素的层叠顺序 ::before::after 伪元素的默认层叠顺序是根据它们的出现顺序来决定的。如果你需要改变它们的层叠顺序,你可以使用 z-index 属性。
transient props 当你使用 transient props(如 $status)时,你需要确保你在所有使用这个 prop 的地方都使用了新的 prop 名。例如,你需要在 Avatar 组件中将 status 改为 $status
prop 类型 当你使用函数来动态生成样式时,你需要确保你的 props 是正确的类型。例如,size prop 应该是一个字符串,而 $status prop 应该是 "online" 或 "offline"。你可以使用 PropTypes 来检查 prop 的类型。

Avatar.stories.jsx

// 导入 React 和 Avatar 组件,以及一些图片和样式文件。
import React from "react";
import Avatar from ".";
import "../../story.css";
import face1 from "../../assets/images/face-male-1.jpg";
import face2 from "../../assets/images/face-male-2.jpg";
import face4 from "../../assets/images/face-male-4.jpg";

// 定义一个默认的导出对象,它包含了故事的标题和组件。
export default {
  title: "Avatar",
  component: Avatar,
};

// 定义一个名为 "Default" 的故事,它返回一个 Avatar 组件,src prop 被设置为 face1。
export const Default = () => {
  return <Avatar  src={face1} />;
};

// 定义一个名为 "Sizes" 的故事,它返回一个包含四个 Avatar 组件的 div 元素,这四个 Avatar 组件的 size prop 分别被设置为 "48px"、"56px"、"64px" 和 "72px"。
export const Sizes = () => {
  return (
    <div className="row-elements">
      <Avatar size="48px" src={face1} />
      <Avatar size="56px" src={face2} />
      <Avatar size="64px" src={face4} />
      <Avatar size="72px" src={face1} />
    </div>
  );
};

// 定义一个名为 "WithStatus" 的故事,它返回一个包含三个 Avatar 组件的 div 元素,这三个 Avatar 组件的 status prop 分别被设置为 "online"、"offline" 和 "offline",最后一个 Avatar 组件的 size prop 被设置为 "72px",statusIconSize prop 被设置为 "12px"。
export const WithStatus = () => {
  return (
    <div className="row-elements">
      <Avatar src={face1} status="online" />
      <Avatar src={face2} status="offline" />
      <Avatar src={face4} status="offline" size="72px" statusIconSize="12px" />
    </div>
  );
};

安装Hygen

Hygen 是一个快速、简单的代码生成器,它可以帮助你自动化创建新的代码文件或代码片段的过程。Hygen 的目标是让你能够快速地创建新的组件、测试、模块等,而不需要手动复制和粘贴代码。

安装hygen:

npm i -g hygen

初始化hygen:

hygen init self

可以看到在根目录下创建了一些文件:

Loaded templates: D:\nvm\v22.2.0\node_modules\hygen\src\templates
       added: _templates/generator/help/index.ejs.t
       added: _templates/generator/with-prompt/hello.ejs.t
       added: _templates/generator/with-prompt/prompt.ejs.t
       added: _templates/generator/new/hello.ejs.t
       added: _templates/init/repo/new-repo.ejs.t

生成自定义的模板文件夹:

hygen generator new component

在new文件夹下创建好模板文件后:

hygen component new Icon
  • hygen:这是 Hygen 代码生成器的命令行工具。
  • component:这是你在 Hygen 中定义的生成器的名称。在这个例子中,它表示你想要生成一个新的组件。
  • new:这是生成器的动作。在这个例子中,它表示你想要创建一个新的组件。
  • Icon:这是你要生成的新组件的名称。

图标组件

image-20240519204150356

$(({ color }) => color &&fill: ${color};) 是一个箭头函数,它接收一个对象作为参数,并从中解构出 color 属性。如果 color 属性存在(即不是 nullundefinedfalse),那么它就会返回一个字符串 fill: ${color};,这是一个 CSS 声明,用于设置元素的填充颜色。

同样,$(({ opacity }) => opacity &&opacity: ${opacity};) 也是一个箭头函数,它接收一个对象作为参数,并从中解构出 opacity 属性。如果 opacity 属性存在,那么它就会返回一个字符串 opacity: ${opacity};,这是一个 CSS 声明,用于设置元素的透明度。

svg,
  svg * {
    ${({ color }) => color && `fill: ${color};`}
    ${({ opacity }) => opacity && `opacity: ${opacity};`}
  }

引入svg图标作为React组件

在 Create React App 和一些其他的项目模板中,你可以使用 ReactComponent 来导入 SVG 文件作为 React 组件。

但是在Vite中,需要引入插件完成转换操作:React + Vite 项目中如何使用 SVG - 掘金 (juejin.cn)

引入FontAwesomeIcon

安装相关图标库:

npm install --save @fortawesome/react-fontawesome @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons  @fortawesome/free-brands-svg-icons @fortawesome/free-regular-svg-icons

引入到story文件中:

import React from "react";
import Icon from ".";
import SmileIcon from "../../assets/icon/smile.svg?react"
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCommentDots } from '@fortawesome/free-solid-svg-icons'
export default {
  title: "UI 组件/Icon",
  component: Icon,
};

export const Default = () => <Icon icon={SmileIcon}></Icon>;


export const CustomColor = () => <Icon icon={SmileIcon} color="#cccccc"></Icon>;

export const CustomSize = () => <Icon icon={SmileIcon} width={48} height={48}></Icon>;

export const FontAwesome = () => <FontAwesomeIcon icon={faCommentDots}></FontAwesomeIcon>;

文章作者: QT-7274
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 QT-7274 !
评论
  目录