从零开始掌握TagInput标签输入组件的开发与优化技巧

志诚 ☘︎ 组件 阅读 2,809
赞 52 收藏
二维码
手机扫码查看
反馈

优化前:卡得不行

前几天,我在做一个项目时遇到了一个很头疼的问题:TagInput标签输入组件的性能实在是太差了。用户每输入一个字符,页面就会卡顿一下,简直让人无法忍受。尤其是在移动端,用户体验简直是灾难级的。

从零开始掌握TagInput标签输入组件的开发与优化技巧

找到症结了!

为了解决这个问题,我先用Chrome的开发者工具进行了性能分析。我发现每次输入字符时,整个组件都在重新渲染,而且还有大量的DOM操作和事件监听在频繁触发。具体来说,主要问题出在以下几个地方:

  • 每次输入字符时,组件会重新计算所有标签的位置和样式。
  • 事件监听器绑定过多,导致每次输入都会触发大量的回调函数。
  • DOM操作过于频繁,每次都重新生成整个标签列表。

找到了这些问题,我开始尝试各种优化方案。

优化方案一:减少不必要的重渲染

首先,我试着减少不必要的重渲染。原来的代码是这样的:

class TagInput extends React.Component {
  state = { tags: [], input: '' };

  handleInputChange = (e) => {
    this.setState({ input: e.target.value });
  };

  handleKeyDown = (e) => {
    if (e.key === 'Enter') {
      const newTags = [...this.state.tags, this.state.input];
      this.setState({ tags: newTags, input: '' });
    }
  };

  render() {
    return (
      <div>
        {this.state.tags.map((tag, index) => (
          <span key={index} className="tag">{tag}</span>
        ))}
        <input
          value={this.state.input}
          onChange={this.handleInputChange}
          onKeyDown={this.handleKeyDown}
        />
      </div>
    );
  }
}

这段代码中,每次输入字符时,handleInputChange 都会触发 setState,进而导致整个组件重新渲染。为了减少重渲染,我使用了 useMemouseCallback 来优化:

import React, { useState, useMemo, useCallback } from 'react';

const TagInput = () => {
  const [tags, setTags] = useState([]);
  const [input, setInput] = useState('');

  const handleInputChange = useCallback((e) => {
    setInput(e.target.value);
  }, []);

  const handleKeyDown = useCallback((e) => {
    if (e.key === 'Enter') {
      const newTags = [...tags, input];
      setTags(newTags);
      setInput('');
    }
  }, [tags, input]);

  const renderedTags = useMemo(() => {
    return tags.map((tag, index) => (
      <span key={index} className="tag">{tag}</span>
    ));
  }, [tags]);

  return (
    <div>
      {renderedTags}
      <input
        value={input}
        onChange={handleInputChange}
        onKeyDown={handleKeyDown}
      />
    </div>
  );
};

export default TagInput;

通过这种方式,renderedTags 只有在 tags 发生变化时才会重新计算,减少了不必要的重渲染。

优化方案二:减少DOM操作

接下来,我尝试减少DOM操作。原来的代码中,每次输入字符时都会重新生成整个标签列表。为了避免这种情况,我引入了虚拟DOM的概念,并使用 React.memo 来优化子组件:

import React, { memo } from 'react';

const Tag = ({ tag }) => {
  return <span className="tag">{tag}</span>;
};

const MemoizedTag = memo(Tag);

const TagInput = () => {
  const [tags, setTags] = useState([]);
  const [input, setInput] = useState('');

  const handleInputChange = useCallback((e) => {
    setInput(e.target.value);
  }, []);

  const handleKeyDown = useCallback((e) => {
    if (e.key === 'Enter') {
      const newTags = [...tags, input];
      setTags(newTags);
      setInput('');
    }
  }, [tags, input]);

  return (
    <div>
      {tags.map((tag, index) => (
        <MemoizedTag key={index} tag={tag} />
      ))}
      <input
        value={input}
        onChange={handleInputChange}
        onKeyDown={handleKeyDown}
      />
    </div>
  );
};

export default TagInput;

通过 React.memo,只有当 tag 发生变化时,MemoizedTag 组件才会重新渲染,大大减少了DOM操作。

优化方案三:优化事件监听

最后,我优化了事件监听器。原来的代码中,每个事件监听器都绑定了大量回调函数。为了减少事件监听器的数量,我将多个事件合并到一个处理函数中:

const TagInput = () => {
  const [tags, setTags] = useState([]);
  const [input, setInput] = useState('');

  const handleInput = useCallback((e) => {
    if (e.type === 'change') {
      setInput(e.target.value);
    } else if (e.type === 'keydown' && e.key === 'Enter') {
      const newTags = [...tags, input];
      setTags(newTags);
      setInput('');
    }
  }, [tags, input]);

  return (
    <div>
      {tags.map((tag, index) => (
        <MemoizedTag key={index} tag={tag} />
      ))}
      <input
        value={input}
        onChange={handleInput}
        onKeyDown={handleInput}
      />
    </div>
  );
};

export default TagInput;

通过这种方式,我将 onChangeonKeyDown 的处理逻辑合并到了一个 handleInput 函数中,减少了事件监听器的数量。

优化后:流畅多了

经过以上几轮优化,TagInput 标签输入组件的性能得到了显著提升。加载时间从原来的5秒左右降到了800毫秒左右,用户体验也得到了极大的改善。特别是在移动端,输入变得非常流畅,没有了之前的卡顿感。

性能数据对比

以下是优化前后的性能数据对比:

  • 优化前:加载时间5秒,CPU占用率高,页面卡顿严重。
  • 优化后:加载时间800毫秒,CPU占用率低,页面流畅。

可以看到,优化后的性能提升非常明显。

总结

以上就是我对TagInput标签输入组件的性能优化经验。通过减少不必要的重渲染、减少DOM操作和优化事件监听,最终实现了较好的性能提升。当然,这里还有很多可以继续优化的地方,比如使用Web Workers来处理一些复杂的计算任务,或者进一步优化CSS样式等。如果你有更好的优化方案,欢迎在评论区交流讨论。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论
萌新.培静
CSS Grid 和 Flex 哪个更适合?
点赞 1
2026-02-13 14:25
司徒姿言
读完这篇文章,我对自己的工作有了新的认识,不再觉得只是在写代码。
点赞 6
2026-02-12 13:25