如何在React中构建动态下拉组件 - 解释React复合组件模式

下拉菜单长期以来一直是网站和应用程序中的重要组成部分。它们是用户交互的默默英雄,通过简单的点击或轻触默默地促进着无数的操作和决策。

今天你可能已经遇到了其中之一,无论是在你最喜爱的在线商店上选择类别,还是在注册表单上选择你的出生日期。

但如果我告诉你,有一个秘密配方可以将你的下拉菜单从平凡提升到华丽呢?

加入我,我将解剖复合组件模式的奥秘,并利用其能力构建一个动态下拉组件。

(本文视频讲解:java567.com)

先决条件

  • HTML、CSS和Tailwind CSS的基础知识
  • React和React Hooks的基础知识。

我们将涵盖的内容:

  1. 理解下拉菜单组件
  2. 理解复合组件
  3. 如何构建下拉菜单组件
    • 常规函数式 React 方法
    • 复合组件模式方法
  4. 常规方法和复合组件方法的比较
  5. 结论

理解下拉菜单组件

下拉菜单组件在用户界面设计中起着至关重要的作用,作为交互式菜单,它们赋予用户从一系列选项中进行选择的能力。通常,它们由一个可点击的区域组成,在激活时展示用户可以进行选择的一系列选项。

下拉菜单组件的操作很简单:当用户与之交互时——通常通过点击或轻触——下拉菜单会展开,显示可用的选项。

随后,用户可以从这些选择中选择一个,然后该选择要么显示在下拉菜单本身中,要么用于更新界面中相关字段或元素。

下拉菜单组件提供了一种清晰高效的方法,向用户呈现各种选择,使其非常适用于需要同时保持整洁界面的情况下访问多个选项的场景。

下拉菜单还具有以下作用:

  • 导航辅助:作为导航辅助工具,下拉菜单通过提供跳转到不同部分或页面的菜单来帮助用户浏览网站。
  • 表单输入:简化数据输入,下拉菜单为用户提供了预定义选项供选择,例如在账户注册期间选择国家、出生日期或首选语言。
  • 过滤器:在电子商务平台上,下拉菜单使购物者能够通过选择选项,如产品类别、价格范围或品牌,来细化其搜索结果。
  • 菜单选择器:在餐厅网站上常见的,下拉菜单显示菜单或允许用户选择菜系类型,便于轻松探索和选择餐饮选项。
  • 数据展示:下拉菜单可以有效地组织和展示数据,允许用户通过日期范围、地理区域或产品类别等条件在仪表板或分析工具中过滤信息。

下拉菜单组件的示例可在此处看到:

01-展示下拉菜单演示展示下拉菜单演示

或在Semantic UI页面上。

理解复合组件

复合组件模式就像使用乐高积木一样:你组装小的部件来创建更大更复杂的东西。在React中,这是一种巧妙的设计组件的方式,由几个小部件组成,它们能够无缝地协同工作。

想象一下你正在构建一个下拉菜单。与其创建一个处理所有事情的单一组件,不如将其拆分成较小、可重用的部件。你可能有一个用于下拉按钮的组件,另一个用于选项列表,还有一个用于处理状态和交互逻辑的组件。

01-复合组件示意图复合组件示意图

这里有一个有趣的地方:这些小组件通过共享上下文进行通信。上下文就像是一个信使,可以在不需要通过组件树的每个级别传递信息的情况下,将信息从一个组件传递到另一个组件。

这是一个强大的工具,可以简化组件之间共享数据的过程,特别是当它们被深度嵌套时。

那么,为什么这种模式如此有益呢?

  • 首先,它提高了可读性。通过将复杂的组件分解为更小、更专注的部件,代码变得更易于理解和维护。每个组件都有明确的责任,这使得调试和更新变得更加容易。
  • 其次,复合组件增强了可维护性。由于组件的每个部分都处理特定的任务,因此进行更改或添加新功能变得更加简单。你可以修改组件的一个部分而不影响其他部分,减少引入错误的风险。
  • 最后,复合组件提供了极大的灵活性。你可以组合不同的部件来创建组件的特殊版本,而无需重写任何代码。这使得调整组件以适应不同目的和设计需求变得更加容易。

因此,虽然使用上下文来构建UI组件的想法起初可能看起来不同寻常,但这是一种巧妙的方式,可以创建动态和可重用的组件,从而赋予开发人员构建出色用户体验的能力。

在接下来的部分中,我们将深入探讨如何使用上下文将复合组件带入实际。

如何构建下拉菜单组件

我已经准备了一个GitHub存储库,其中包含启动文件,以加快进度。只需克隆这个存储库并安装依赖项。

在这一部分,我们将使用常规的函数式React构建一个下拉菜单组件,然后将其与CC模式进行比较,以充分理解它们之间的区别。PS:你一定会喜欢复合组件模式的。😁

02-Oh-fo-sho-memeOh Fo sho Snoop Dogg gif

常规函数式React方法

我们将从创建下拉菜单组件的基本结构开始。这将涉及设置主下拉菜单容器、触发下拉菜单的按钮以及选项列表。

const Dropdown = () => {
  return (
    <div>
      <label className="mt-4">为任务分配用户:</label>

      <button className="  px-4 w-full py-2 flex items-center justify-between  rounded border border-[#828FA340] hover:border-primary cursor-pointer relative ">
        <span className="block">
          <FiChevronDown color="#635FC7" size={24} />
        </span>
      </button>
    </div>
  );
};

这将呈现:

02-Dropdown-button-rendered下拉按钮呈现

然后将用户数组传递给下拉菜单,以创建用户列表。

const Dropdown = ({ usersArray }) => {
  return (
    <div>
      <label className="mt-4">为任务分配用户:</label>

      <button className="  px-4 w-full py-2 flex items-center justify-between  rounded border border-[#828FA340] hover:border-primary cursor-pointer relative ">
        <span className="block">
          <FiChevronDown color="#635FC7" size={24} />
        </span>
        {
          <div className="absolute bottom-full translate-x-9  left-full translate-y-full rounded bg-[#20212c] w-max">
            <ul className="flex flex-col p-2">
              {usersArray.map((user) => (
                <li
                  key={user.id}
                  className={`flex items-center gap-2 p-4 hover:bg-[#2b2c37] rounded transition-all duration-200 `}>
                  <img
                    className="w-6 h-6 "
                    src={user.imgUrl}
                    alt={`${user.name} image`}
                  />
                  <span>{user.name}</span>
                </li>
              ))}
            </ul>
          </div>
        }
      </button>
    </div>
  );
};

这将呈现:

03-Dropdown-list-rendered下拉列表呈现

目前,你的下拉列表默认显示。为了添加切换行为,请为其可见性创建一个状态。

 const [isDropdownOpen, setIsDropdownOpen] = useState(false);

然后将它们作为props传递给Dropdown组件。

<Dropdown
 usersArray={usersArray}
 isDropdownOpen={isDropdownOpen}
 setIsDropdownOpen={setIsDropdownOpen}
 />

在看到结果之前,将一个切换函数附加到下拉按钮,用于将下拉状态更改为true。

const toggleDropdown = () => {
    setIsDropdownOpen(true);
};

现在你的下拉组件应该是这样的:

const Dropdown = ({ usersArray, setIsDropdownOpen, isDropdownOpen }) => {
  const toggleDropdown = () => {
    setIsDropdownOpen(true);
  };

  return (
    <div>
      <label className="mt-4">为任务分配用户:</label>

      <button
        className="  px-4 w-full py-2 flex items-center justify-between  rounded border border-[#828FA340] hover:border-primary cursor-pointer relative "
        // Function to show the dropdown on click
        onClick={toggleDropdown}>
        <span className="block">
          <FiChevronDown color="#635FC7" size={24} />
        </span>
	  // Conditionally rendering your dropdown list
        {isDropdownOpen && (
          <div className="absolute bottom-full translate-x-9  left-full translate-y-full rounded bg-[#20212c] w-max">
            <ul className="flex flex-col p-2">
              {usersArray.map((user) => (
                <li
                  key={user.id}
                  className={`flex items-center gap-2 p-4 hover:bg-[#2b2c37] rounded transition-all duration-200 `}>
                  <img
                    className="w-6 h-6 "
                    src={user.imgUrl}
                    alt={`${user.name} image`}
                  />
                  <span>{user.name}</span>
                </li>
              ))}
            </ul>
          </div>
        )}
      </button>
    </div>
  );
};

你的下拉现在的行为是这样的:

03-Dropdown-with-list-conditionally-rendering有条件地呈现下拉列表的下拉菜单

我知道你已经注意到你的下拉只能打开,而不能关闭。别担心,我们稍后会以更加简洁的方式来修复它。😉

04-Trust-the-process相信过程

接下来,让我们创建一种方法来为任务分配用户。首先,在App组件中创建一个状态以存储已分配的用户。

 const [assignedList, setAssignedList] = useState([]);

然后将其作为props传递给Dropdown组件。

<Dropdown
  usersArray={usersArray}
  isDropdownOpen={isDropdownOpen}
  setIsDropdownOpen={setIsDropdownOpen}
  assignedList={assignedList}
  setAssignedList={setAssignedList}
/>

要为任务分配用户,创建一个处理函数,首先检查你试图添加的用户是否已经在数组中,如果还没有,将其添加进去,如果已经存在,则将其移除。

  function handleAssign(user) {
    setAssignedList((prevList) => {
      // 检查用户是否已存在于列表中
      if (prevList.includes(user)) {
        // 如果用户存在,则从列表中移除它
        const updatedList = prevList.filter((item) => item !== user);
        return updatedList;
      } else {
        // 如果用户不存在,则将其添加到列表中
        return [...prevList, user];
      }
    });
  }

为了确认这个函数是否有效,使用assignedList数组为每个已分配的用户添加一个检查图标。

<ul className="flex flex-col p-2">
  {usersArray.map((user) => (
    <li
      key={user.id}
      className={`flex items-center gap-2 p-4 hover:bg-[#2b2c37] rounded transition-all duration-200 `}
      onClick={() => handleAssign(user)}
    >
      {assignedList.includes(user) && <FiCheck />}

      <img
        className="w-6 h-6 "
        src={user.imgUrl}
        alt={`${user.name} image`}
      />
      <span>{user.name}</span>
    </li>
  ))}
</ul>

通过这个改变,下拉应该会在点击每个用户时进行分配和取消分配。

05-Assigning-and-unassigning-users-to-the-task为任务分配和取消分配用户

为了改进UI,让我们创建一个组件来显示所有已分配的用户。

创建一个AssignedList组件,并传递其相应的状态。

 <AssignedList
   assignedList={assignedList}
   setAssignedList={setAssignedList}
  />

然后使用已分配的数组来创建一些JSX。

function AssignedList({ assignedList, setAssignedList }) {
  return (
    <div className="mt-4 p-2 shadow-sm bg-[#828fa318] rounded">
      <h2 className="px-2 my-3 font-bold">已分配列表:</h2>
      <div className="flex flex-wrap gap-4 ">
        {assignedList?.map((user, index) => (
          <div
            key={user.id}
            className="flex items-center gap-1 w-[47.5%] p-2 hover:bg-[#20212c] rounded transition-all duration-200">
            <span>{index + 1}.</span>
            <img
              className="w-6 h-6 "
              src={user.imgUrl}
              alt={`${user.name} image`}
            />

            <span>{user.name}</span>
            <span className="ml-auto cursor-pointer p-1 hover:bg-[#2b2c37] rounded-full">
              <FaXmark />
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}

现在测试你的组件应该得到:

06-Displaying-assigned-users-using-the-AssignedList-component使用AssignedList组件显示已分配的用户

最后一个改变是一个主观的选择,因为我更喜欢在没有当前有用户分配任务时显示其他内容。

{assignedList.length === 0 ? (
  <p className="mt-4 p-2 shadow-sm bg-[#828fa318] rounded">
    尚未为任务分配用户。
  </p>
) : (
  <AssignedList
    assignedList={assignedList}
    setAssignedList={setAssignedList}
  />
)}

这将带来UI:

10-Showing-a-default-text-when-no-users-are-assigned在没有用户被分配时显示默认文本

复合组件模式方法

现在,让我们开始主要内容。首先创建一个包装整个组件的上下文。

const UserAssignContext = createContext();

然后收集我们的下拉菜单及其组件所需的所有必要数据和函数。这包括已分配用户列表、更新该列表的函数以及下拉菜单当前是否打开等内容。

然后,将这些值提供给所有子组件。

const UserAssignDropdown = ({
  children,
  assignedList,
  setAssignedList,
  users,
}) => {
  const UserAssignDropdownRef = useRef(null);
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);

  return (
    <UserAssignContext.Provider
      value={{
        assignedList,
        users,
        UserAssignDropdownRef,
        isDropdownOpen,
        setIsDropdownOpen,
        setAssignedList,
      }}>
      <div ref={UserAssignDropdownRef}>{children}</div>
    </UserAssignContext.Provider>
  );
};

有了上下文设置好了,现在是时候制作组成我们下拉菜单的各个组件了。每个组件将与上下文交互,以访问和操作必要的数据和函数。

首先,从我们刚刚构建的组件中复制每个样式。

头部组件

这个组件保持不变。

const Header = () => {
  return <label className="mt-4 mb-2 text-sm">为任务分配用户:</label>;
};
关闭组件

该组件从上下文中获取用于切换下拉菜单的函数。

const Close = () => {
  const { setIsDropdownOpen } = useContext(UserAssignContext);
  return (
    <div
      className="absolute top-0 right-0 flex items-center justify-center -translate-y-full gap-2 bg-[#C0392B] px-2 py-1 rounded-t"
      onClick={(e) => {
        e.stopPropagation();
        setIsDropdownOpen(false);
      }}>
      <span>关闭</span>
      <span>
        <FaXmark size={20} />
      </span>
    </div>
  );
};
已分配列表组件

该组件显示已分配的用户列表,并从列表中删除用户。

const AssignedList = () => {
  const { assignedList, setAssignedList } = useContext(UserAssignContext);

  function handleRemove(id) {
    setAssignedList((assignedList) =>
      assignedList.filter((user) => user.id !== id)
    );
  }

  if (assignedList.length === 0)
    return (
      <p className="mt-4 p-2 shadow-sm bg-[#828fa318] rounded">
        尚未为任务分配用户。
      </p>
    );

  return (
    <div className="mt-4 p-2 shadow-sm bg-[#828fa318] rounded">
      <h2 className="px-2 my-3 font-bold">已分配列表:</h2>
      <div className="flex flex-wrap gap-4 ">
        {assignedList?.map((user, index) => (
          <div
            key={user.id}
            className="flex items-center gap-1 w-[47.5%] p-2 hover:bg-[#20212c] rounded transition-all duration-200"
            onClick={() => handleRemove(user.id)}>
            <span>{index + 1}.</span>
            <img
              className="w-6 h-6 "
              src={user.imgUrl}
              alt={`${user.name} image`}
            />

            <span>{user.name}</span>
            <span className="ml-auto cursor-pointer p-1 hover:bg-[#2b2c37] rounded-full">
              <FaXmark />
            </span>
          </div>
        ))}
      </div>
    </div>
  );
};
项组件

该组件表示每个用户,并具有添加和从已分配列表中删除用户的功能。

const Item = ({ user }) => {
  const { assignedList, setAssignedList } = useContext(UserAssignContext);

  function handleAssign(user) {
    setAssignedList((prevList) => {
      // 检查用户是否已存在于列表中
      if (prevList.includes(user)) {
        // 如果用户存在,则从列表中移除它
        const updatedList = prevList.filter((item) => item !== user);
        return updatedList;
      } else {
        // 如果用户不存在,则将其添加到列表中
        return [...prevList, user];
      }
    });
  }

  return (
    <li
      key={user.id}
      className={`flex items-center gap-2 p-4 hover:bg-[#2b2c37] rounded transition-all duration-200 `}
      onClick={() => handleAssign(user)}>
      {assignedList.includes(user) && <FiCheck />}

      <img className="w-6 h-6 " src={user.imgUrl} alt={`${user.name} image`} />
      <span>{user.name}</span>
    </li>
  );
};
按钮组件

该组件控制显示List组件(浮动下拉列表)。

const Button = () => {
  const { setIsDropdownOpen } = useContext(UserAssignContext);
  return (
    <button
      className="  px-4 py-2 flex items-center justify-between w-full rounded border border-[#828FA340] hover:border-primary cursor-pointer relative "
      onClick={() => setIsDropdownOpen(true)}>
      <span className="block">
        <FiChevronDown color="#635FC7" size={24} />
      </span>

      <UserAssignDropdown.List />
    </button>
  );
};

要将这些组件组合成一个单一的复合组件,你需要将每个组件分配给父组件,如下所示:

UserAssignDropdown.List = ListContainer;
UserAssignDropdown.Item = Item;
UserAssignDropdown.Header = Header;
UserAssignDropdown.Button = Button;
UserAssignDropdown.AssignedList = AssignedList;
UserAssignDropdown.Close = Close;

接下来,在你的App组件中作为包装组件导入你的复合组件,并传入适当的状态。

export default function App() {
  const [assignedList, setAssignedList] = useState([]);

  return (
    <div className="bg-[#2b2c37] h-[100dvh] text-white flex  p-20 gap-4 items-center flex-col">
      <div className=" w-[400px] ">
        <h1 className="text-2xl ">复合组件模式</h1>
        <UserAssignDropdown
          assignedList={assignedList}
          setAssignedList={setAssignedList}
          users={usersArray}></UserAssignDropdown>
      </div>
    </div>
  );
}

然后在包装器中呈现适当的子组件。

export default function App() {
  const [assignedList, setAssignedList] = useState([]);

  return (
    <div className="bg-[#2b2c37] h-[100dvh] text-white flex  p-20 gap-4 items-center flex-col">
      <div className=" w-[400px] ">
        <h1 className="text-2xl ">复合组件模式</h1>
        <UserAssignDropdown
          assignedList={assignedList}
          setAssignedList={setAssignedList}
          users={usersArray}>
          <UserAssignDropdown.Header />
          <UserAssignDropdown.Button />
          <UserAssignDropdown.AssignedList />
        </UserAssignDropdown>
      </div>
    </div>
  );
}

最后,使用之前创建的自定义钩子在单击组件外部时关闭下拉菜单。

const UserAssignContext = createContext();
const UserAssignDropdown = ({
  children,
  assignedList,
  setAssignedList,
  users,
}) => {
  const UserAssignDropdownRef = useRef(null);
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);

  useClickOutside(UserAssignDropdownRef, () => {
    setIsDropdownOpen(false);
  });

  return (
    <UserAssignContext.Provider
      value={{
        assignedList,
        users,
        UserAssignDropdownRef,
        isDropdownOpen,
        setIsDropdownOpen,
        setAssignedList,
      }}>
      <div ref={UserAssignDropdownRef}>{children}</div>
    </UserAssignContext.Provider>
  );
};

至此,你的组件就完成了相同的功能!

11-replicating-the-same-funtionality-with-the-compound-component-pattern复制相同功能的复合组件模式

但是为什么要停在这里呢?

使用这种模式,更改组件的外观就像更改它们在父组件中呈现顺序一样简单。例如,如果你想要首先显示按钮,你只需在父组件中更改顺序。

<UserAssignDropdown
  assignedList={assignedList}
  setAssignedList={setAssignedList}
  users={usersArray}
>
  <UserAssignDropdown.Button />
  <UserAssignDropdown.Header />
  <UserAssignDropdown.AssignedList />
</UserAssignDropdown>

UI会相应地作出反应。

04-order-of-rendering-in-compound-component-changed复合组件中呈现顺序已更改

此组件还灵活到足以通过 props 更改元素的布局。

只需通过父级传递样式 props:

<UserAssignDropdown
  assignedList={assignedList}
  setAssignedList={setAssignedList}
  users={usersArray}
>
  <UserAssignDropdown.Header />
  <UserAssignDropdown.Button
    listStyles={"!-left-5 !-translate-x-full bg-[#605e80] text-white border"}
  />
  <UserAssignDropdown.AssignedList />
</UserAssignDropdown>

并在子级接收这些 props:

const Button = ({ listStyles }) => {
  const { setIsDropdownOpen, UserAssignDropdownRef } =
    useContext(UserAssignContext);
  return (
    <button
      className="  px-4 py-2 flex items-center justify-between w-full rounded border border-[#828FA340] hover:border-primary cursor-pointer relative "
      ref={UserAssignDropdownRef}
      onClick={() => setIsDropdownOpen(true)}>
      <span className="block">
        <FiChevronDown color="#635FC7" size={24} />
      </span>
      <UserAssignDropdown.List listStyles={listStyles} />
    </button>
  );
};

const ListContainer = ({ listStyles }) => {
  const { users, isDropdownOpen } = useContext(UserAssignContext);

  return (
    isDropdownOpen && (
      <ul
        className={`absolute bottom-full translate-x-9  left-full translate-y-full rounded bg-[#20212c] w-max ${listStyles}`}>
        <UserAssignDropdown.Close />
        <div className="flex flex-col p-2">
          {users?.map((user, index) => (
            <UserAssignDropdown.Item key={index} user={user} />
          ))}
        </div>
      </ul>
    )
  );
};

你可以轻松更改组件的外观。

12-Using-props-to-customize-the-compound-component使用 props 自定义复合组件

常规方法与复合组件方法的比较

好的,让我们退后一步,比较一下我们刚刚探讨过的两种方法。

简单性和组织性

  • 常规方法:想象一下就像在一个大碗里一次性烘焙蛋糕一样。使用常规方法,我们可以创建一个负责下拉菜单中所有内容的单个组件 - 按钮、列表和所有配料。就像有一个大型食谱卡片,所有步骤都混在一起。它能完成任务,但有点凌乱,而且很难跟踪,特别是当你试图调整食谱的某一部分时。
  • 复合组件方法:现在想象一下,我们为每个配料都有不同的碗,一个用于面粉,另一个用于糖,依此类推。这就是复合组件模式。下拉菜单的每个部分都有自己的空间来发光。就像组织你的厨房一样 - 每样东西都有它的位置。这使得理解和修改变得更容易。需要更改面粉?你知道该去哪里找。

灵活性和定制性

  • 常规方法:使用我们的单碗方法,对下拉菜单的特定部分进行更改有点像尝试在那个大蛋糕混合物中交换成分。当然,你可以做到,但并不总是容易。想要不同口味的蛋糕?你可能需要深入整个碗找到添加它的地方。
  • 复合组件方法:使用复合组件模式,就像为每种口味都有单独的容器。需要添加巧克力片?只需拿起巧克力容器,撒上即可。每个组件都有它的工作,使得定制变得更简单。想要更改按钮的颜色?没问题,它就在那里的容器里。

重用和维护

  • 常规方法:当你的食谱都混在一个碗里时,很难将其中的一部分重用于另一道菜。而且,随着厨房变得越来越忙,东西很容易变得凌乱,难以跟踪。每当你想要制作新菜品时,你可能会发现自己需要重写食谱。
  • 复合组件方法:使用复合组件模式,就像在你的厨房里有一套可重用的工具一样。需要制作不同种类的蛋糕?只需拿起你需要的工具,开始烘焙。每个组件就像是一个专业的小工具 - 容易重用和维护。而且当你的厨房井然有序时,制作新菜品就轻而易举了。

附加信息

这里是你可能需要的所有资源链接。

  • 起始文件
  • 常规函数模式
  • 复合组件模式

结论

最后,这两种方法都在你的代码中有其用武之地。常规方法就像你可靠的旧搅拌碗 - 可靠而熟悉,但也许并不适合每种食谱。

复合组件模式就像一个组织良好的厨房,一切井然有序,准备就绪。它可能需要一些设置,但从长远来看,它可以让你的生活变得更轻松。所以,根据你要做的事情,选择适合你口味的方法 - 并愉快地编码吧! 🍰🎨

(本文视频讲解:java567.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/567034.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

106短信群发平台如此火热究竟有没有效?

106短信群发平台之所以如此火热&#xff0c;确实是因为它在多个方面展现出了显著的有效性。 首先&#xff0c;从发送速度和到达率来看&#xff0c;106短信平台表现优秀。无论是节假日还是平日&#xff0c;其发送速度都能保持在一个较快的水平&#xff0c;这对于验证码短信、通…

Mybatis源码学习系列之搭建源码阅读环境

1. 实验环境 jdk1.8.0_171apache-maven-3.5.4IntelliJ IDEA 2018mybatis-parent 31mybatis-3.5.4 2. 下载源码 先在官方GitHub下载对应的代码 git clone https://github.com/mybatis/parent git clone https://github.com/mybatis/mybatis-3mybatis3使用3.5.4版本&#xff…

【数据库教程】Navicat和在线phpMyAdmin如何连接远程服务器

文章目录 NavicatphpMyAdmin来源 有很多工具可以连接数据库&#xff0c;这里介绍两种&#xff0c;自行选择一种使用即可 Navicat 1、打开Navicat 2、点击连接&#xff0c;选择MySQL 3、输入数据库信息后点击测试连接&#xff08;数据库信息请联系我&#xff09; 4、点击确…

Java进阶-Stream流

概述 在Java8中&#xff0c;得益于lambda所带来的函数式编程&#xff0c;引入了一个全新的Stream流的概念目的&#xff1a;用于简化集合和数组操作的api 案例 需求&#xff1a;创建一个集合存储多个字符串元素&#xff0c;将集合中所有以“z”开头的元素存储到新的集合中&am…

C++ | Leetcode C++题解之第44题通配符匹配

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isMatch(string s, string p) {auto allStars [](const string& str, int left, int right) {for (int i left; i < right; i) {if (str[i] ! *) {return false;}}return true;};auto charMatch []…

LPA算法简介

1. 背景 标签传播算法(Label Propagation Algorithm)是一种基于图的半监督学习方法&#xff0c;其基本思路是用已标记节点的标签信息去预测未标记节点的标签信息。 2. 算法流程 1. 为每个节点随机的指定一个自己特有的标签&#xff1b; 2. 逐轮刷新所有节点的标签&#xff0…

设计模式(四):单例模式

设计模式&#xff08;四&#xff09;&#xff1a;单例模式 1. 单例模式的介绍2. 单例模式的类图3. 单例模式的实现3.1 懒汉式&#xff08;线程不安全&#xff09;3.2 懒汉式&#xff08;线程安全&#xff09;3.3 饿汉式3.4 静态内部类3.5 枚举 1. 单例模式的介绍 单例模式&…

SQL-DML数据操纵语言(Oracle)

文章目录 DML数据操纵语言常见的字段属性字符型字段属性char(n)varchar2(n)/varchar(n) 数值型字段属性number([p],[s]int 日期型字段属性DATEtimestamp 如何查看字段属性增加数据INSERT快捷插入 删除数据DELETE修改数据UPDATE DML数据操纵语言 定义 是针对数据做处理&#xf…

信息系统项目管理49个过程、查看工具与技术!最好用的工具

高项【浏览器打开&#xff0c;用于默写49个过程、查看工具与技术】.html 下载html后建议使用Edge、chrome或者火狐浏览器打开   查看工具与技术&#xff1a;鼠标左键单击管理活动&#xff1b;关闭工具与技术&#xff1a;点击工具与技术外面退回 1、点击【开始默写】按钮&…

在国企上班,有必要考软考吗?

现在很多在私企工作的朋友都会参加软考&#xff0c;国企员工更是如此。软考可以以考代评&#xff0c;传统的职称获取需要两步&#xff0c;第一步是评审&#xff0c;第二步是单位聘任。而通过软考取得证书就可以省去第一步&#xff0c;只需获得单位聘任即可享受相应的职称福利。…

Ventus(承影):基于RISC V的开源GPGPU

Ventus&#xff08;承影&#xff09;&#xff1a;基于RVV的开源GPGPU 清华大学集成电路学院dsp-lab的承影RVV GPGPU设计文档。 整体目标 提供一个开源的基于RVV的GPGPU实现方案&#xff0c;并给出软件映射方案、指令集&#xff08;支持的指令及特性、添加的自定义指令&#xf…

路由引入,路由过滤,路由策略实验

1&#xff0c;配置IP地址 R1&#xff1a; [R1]dis ip interface brief Interface IP Address/Mask Physical Protocol GigabitEthernet0/0/0 100.1.1.1/24 up up LoopBack0 …

OpenHarmony实战开发-如何实现tabContent内容可以在tabBar上显示并且tabBar可以响应滑动事件的功能。

介绍 本示例实现了tabContent内容可以在tabBar上显示并且tabBar可以响应滑动事件的功能。 效果图预览 使用说明 1.点击播放按钮进行视频播放&#xff0c;按住进度条按钮和进度条下方区域可以拖动进度条&#xff0c;更改视频播放进度。 实现思路 原生的Tabs组件&#xff0c…

IP地址SSL证书的申请流程——五步轻松实现https

没有域名或者不方便提供域名&#xff0c;只有IP地址也可以申请SSL证书&#xff0c;为IP地址申请ssl证书是需要开放443或者80端口&#xff0c;一般开放几分钟用来验证IP管理权即可&#xff01; 具体流程如下 IP地址证书 ssl证书点击这里直接申请 https://www.joyssl.com/certif…

鸿蒙应用开发之Web组件6

前面学习怎么样设置Web界面显示不同的颜色配置,这种适合不同时间来设置,比如白天要亮一些,晚上要暗一些。现在来学习使用Web组件选择文件文件列表的功能。 这个功能主要就是使用在Web打开一个页面,然后有上传文件的按钮,比如下面的界面: 当用户点击选择文件按钮时,就会…

nvidia-smi 输出内容详解

一、nvidia-smi 介绍 nvidia-smi&#xff08;NVIDIA System Management Interface&#xff09;是一种命令行实用程序&#xff0c;主要用于监控和管理NVIDIA GPU&#xff08;图形处理器&#xff09;的状态和性能。它提供了一个简单而强大的方式来获取有关GPU的实时信息&#xf…

javaWeb项目-财务管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、Springboot框架 …

管理集群工具之LVS

管理集群工具之LVS 集群概念 将很多机器组织在一起&#xff0c;作为一个整体对外提供服务集群在扩展性、性能方面都可以做到很灵活集群分类 负载均衡集群&#xff1a;Load Balance高可用集群&#xff1a;High Availability高性能计算&#xff1a;High Performance Computing …

面向对象练习坦克大兵游戏

游戏玩家&#xff08;名称&#xff0c;生命值&#xff0c;等级&#xff09;&#xff0c;坦克&#xff0c;大兵类&#xff0c;玩家之间可以相互攻击&#xff0c;大兵拥有武器&#xff0c;用枪弹和反坦克炮弹&#xff0c;造成攻击不同&#xff0c;坦克攻击值固定&#xff0c;请设…

Java 源码-多级时间轮TimingWheel

多级时间轮TimingWheel 一、时间轮是什么 类似现实中的钟表&#xff0c;由多个环形数组组成&#xff0c;每个环形数组包含20个时间单位&#xff0c;表示一个时间维度&#xff08;一轮&#xff09;&#xff0c;如&#xff1a;第一层时间轮&#xff0c;数组中的每个元素代表1m…