gpt4 book ai didi

javascript - React Text Clamp 的性能改进?

转载 作者:可可西里 更新时间:2023-11-01 02:10:19 31 4
gpt4 key购买 nike

我正在尝试制作一个可重用的 React text-clamp 组件。用户传入要呈现的行数和他们想要显示的文本,然后组件呈现他们的文本,在指定的行数处将其 chop 并在末尾插入省略号 (...)。

我计算在哪里 chop 文本和插入省略号的方法是一次添加一个单词,直到 clientHeight文本大于 clientHeight容器 div。

虽然它有效,但我在 chrome 开发工具中看到以下内容:

[Violation] Forced reflow while executing JavaScript took 179ms .

这可能是因为阅读 clientHeight forces reflow .

这是我的代码:

class TextClamp extends React.PureComponent {

constructor(props) {
super(props);
this.renderText = this.renderText.bind(this);
this.state = {
words: this.props.textToDisplay.split(' '),
};
}

componentDidMount() {
this.renderText();
}

renderText(isResizing = false) {
const textEl = this.displayedText;
const clampContainer = this.clampContainer;
const heightToStop = isResizing ? clampContainer.style.height : this.letterHeightText.clientHeight * this.props.linesToRender;
const dummyText = this.dummyText;
const dummyDiv = this.dummyDiv;
const words = this.state.words;
const numWords = words.length;
dummyDiv.style.cssText = `width: ${clampContainer.clientWidth}px; position: absolute; left: -1000px;`;

let i = this.props.estimatedWordCount || 20;
let strToRender = words.slice(0, i).join(' ');
dummyText.textContent = strToRender;
if (dummyText.clientHeight <= heightToStop && i>=numWords) {
return;
}
while (dummyText.clientHeight <= heightToStop && i<numWords) {
dummyText.textContent += ' ' + words[i++];
};
strToRender = dummyText.textContent;
while (dummyText.clientHeight > heightToStop) {
strToRender = strToRender.substring(0, strToRender.lastIndexOf(' '));
dummyText.textContent = strToRender + '\u2026';
}
textEl.textContent = dummyText.textContent;
}

render() {
const estimatedHeight = this.props.estimatedHeight || 20 * this.props.linesToRender;
const containerStyle = { height: estimatedHeight, overflow: 'hidden'};
if (typeof window !== 'undefined') {
const dummyDiv = document.createElement('div');
const dummyText = document.createElement('p');
dummyDiv.appendChild(dummyText);
this.dummyDiv = dummyDiv
this.dummyText = dummyText
document.body.appendChild(dummyDiv);
}
return (
<div style={containerStyle} ref={(input) => {this.clampContainer = input;}}>
<p ref={(input) => {this.displayedText = input;}}>{this.props.textToDisplay}</p>
<p style={{visibility: 'hidden'}} ref={(input) => {this.letterHeightText = input;}}>Q</p>
</div>
);
}
}

所以基本上,组件的主要主力是 renderText()功能。在那里,我一次添加一个词,直到文本的高度大于其容器的高度。从那里,我删除了最后一个词并添加了省略号。

我所做的优化如下:

  1. estimatedWordCount 允许每次添加一个单词的循环不必每次都从头开始。

  2. 我通过将实际容器 div 的尺寸复制到屏幕外来计算应该显示的文本,position:absolute div,因此它与其他 DOM 元素没有交互。

然而,即使经过我的优化,chrome 仍然提示由于 javascript 导致的重排花费的时间太长。

我的 renderText() 是否有任何优化?我可以做的功能以避免阅读 clientHeight这么频繁?

最佳答案

满足规定的要求:

The user passes in the number of lines to render and the text they want to display, and the component renders their text, cutting it off at the specified number of lines and inserting an ellipsis (...) at the end.

一种方法是放弃高度计算而只担心宽度,添加单词直到我们的行宽度与其容器碰撞,并跟踪添加的行直到达到指定行的最大数量。

这种方法大大加快了速度,因为它避免了过多地接触 DOM。有趣的是,我看到渲染时间加快了 3 倍。使用此方法和一些其他优化,请参阅内联注释以了解更多信息。

Take a look at this component这是我编码的,在此处列出以供引用。另请查看下面的示例用法。

import React, {Component} from "react";

class TextClamp extends Component {
constructor(props) {
super(props);
this.state = {
lines: []
}
}

computeText = () => {

// Our desired text width we are trying to hit
const width = this.container.clientWidth;

// we reverse the word list so can take grab elements efficiently using pops
// pops are O(1) while unshift is O(n).
let words = this.props.textToDisplay.split(/\s+/).reverse();

// we keep lines separate, rather than all concatenated together with \n,
// because react will remove new lines unless we resort to using
// dangerouslySetInnerHTML, which we should prefer to avoid
let lines = [];

// we reset any previous text to avoid bugs if we happen to call computeText more than once
this.textContainer.textContent = "";

let lineNumber = 0;

// first word and line init
let word = words.pop();
lines[lineNumber] = "";

// Our goal is to build up the lines array to contain at most
// linesToRender elements, with each line's width being at most
// the width of our container
while (word ) {

// add our word
lines[lineNumber] += " " + word;
this.textContainer.textContent += " " + word;


// too wide, so we instead start a new line
if (this.textContainer.clientWidth >= width) {
// add back the word for the next line
words.push(word);
// remove our last added and clean up
lines[lineNumber] = lines[lineNumber].slice(0, -word.length).trim();

// already at linesToRender, therefore we cannot render complete text,
// so we add our ellipsis
if(lineNumber === this.props.linesToRender-1) {
lines[lineNumber] += " ..."
break;
}

// remove current text so we can calculate our next line width
this.textContainer.textContent = "";

console.log(lineNumber, this.props.linesToRender)


lineNumber++;
// init our next line
lines[lineNumber] = "";
}



// next word
word = words.pop()
console.log(word)
}

// clean up just like we added a new line,
lines[lineNumber] = lines[lineNumber].trim();


// remove current text so when react renders it has a clean slate to add text elements
this.textContainer.textContent = "";

this.setState({
lines: lines,
})
};

componentDidMount() {
this.computeText();
}

render() {

// we need our 'pre for our whiteSpace, to explicitly control when our text breaks
const containerStyle = {whiteSpace: 'pre'};
// we need 'inline-block' so our p tag's width reflects the amount of text added, not its parent
const textStyle = {display: 'inline-block'};

// put line breaks between all the lines, except the first
const lines = this.state.lines.map((text, i) => i ? [<br/>, text] : text);
console.log(this.state.lines)
return (
<div style={containerStyle} ref={(input) => {
this.container = input;
}}>
<p style={textStyle} ref={(input) => {
this.textContainer = input;
}}>
{lines}
</p>
</div>
);
}
}

TextClamp.defaultProps = {
linesToRender: 2,
textToDisplay: ""

};

用法:

const exampleText = "This is an example piece of text. It should properly break lines at the correct width of it's parent, until it a certain max number of lines have been created. However sometimes the text, is too long to fit on the specified number of lines. At that point the line should be cut off."
const lines = 3
<TextClamp linesToRender={lines} textToDisplay={exampleText} />

关于javascript - React Text Clamp 的性能改进?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45091779/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com