Я пытаюсь сделать многоразовый компонент React text-clamp. Пользователь передает количество строк для рендеринга и текст, который они хотят отобразить, и компонент отображает их текст, отсекая его по указанному количеству строк и вставляя в конец многоточие (...).
Как я вычисляю, где отрезать текст и вставить многоточие, нужно добавить одно слово за раз, пока текст clientHeight
больше, чем clientHeight
контейнера div.
Пока он работает, я вижу в инструментах chrome dev следующее:
[Violation] Forced reflow while executing JavaScript took 179ms
.
Это, вероятно, связано с тем, что чтение clientHeight
принудительно завершает.
Здесь мой код:
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()
. Там я добавляю одно слово за раз, пока высота текста больше, чем высота его контейнера. Оттуда я удаляю последнее слово и добавляю многоточие.
Оптимизации, которые я сделал, следующие:
-
evaluationWordCount позволяет циклу добавлять одно слово в то время, когда ему не нужно начинать с самого начала каждый раз.
-
Я вычисляю текст, который должен отображаться, копируя размеры фактического контейнера div на заставку,
position:absolute
div, чтобы он не взаимодействовал с другими элементами DOM.
Однако даже с моей оптимизацией хром все еще жалуется, что переполнение из-за javascript занимает слишком много времени.
Есть ли какие-либо оптимизации для моей функции renderText()
, которую я могу сделать, чтобы не часто читать clientHeight
?