观摩一段简单粗暴的 vue 代码

2020-09-29 23:42:59 +08:00
 px920906

是一个 vue 的 updated 生命周期钩子:

  updated () {
    let tx = document.getElementsByClassName('textContent');
    for (let jj = 0; jj < tx.length; jj++) {
      while (tx[jj].offsetHeight < tx[jj].scrollHeight) {
        let ss = tx[jj].innerText;
        tx[jj].innerText = ss.substr(0, ss.length - 4);
        tx[jj].innerText += '...';
      }
    }
  }

页面是一个展示作品的页面,估计是设计要求作品简介的文字所占高度不超过一定值,超出部分裁掉用三个点代替。 作者的做法是等 vue 拿到数据并重新渲染 dom 之后把页面上所有.textContent 元素里的文字逐字删到所占高度刚好不超过元素高度(.textContent 高度固定)。作品有多条,再加上其他数据更新导致的重新渲染,这段代码会执行多次。

可能他 /她在开发和测试过程中没遇到文字很多的情况,现在我们遇到了一个两万字的数据,导致每次刷新页面至少需要 2 分钟,期间页面处于假死状态。用 chrome 性能面板录制了一下,耗时 4 分多钟,上个截图大伙感受一下:

设置 innerText 本身就是会导致强制同步布局的耗时操作,再循环两万次……

不过光指出问题不够,怎么解决?

我自己还真想不出好办法……不过首先会把这段逻辑放在获取作品数据完成后的一个 nextTick 里,而不是 updated 钩子里。另一个是获取到数据后直接限制文字长度为固定值,但不好保证最后行数以及文字末尾在行末,肯定过不了设计的关。或者,两者结合,先限制为一个尽量短但足够的长度,再执行上面的逻辑。

最后查到了-webkit-line-clamp这个 css 属性,感觉不考虑 IE 的话,就是它了。

大家有什么好的方案?

5964 次点击
所在节点    程序员
49 条回复
Czzzzzzzzzzr
2020-09-30 09:09:09 +08:00
说实话我还从来没有想到过还可以循环来减长度。。
KuroNekoFan
2020-09-30 09:24:35 +08:00
事实上在某些场景下需要精准获得字符占的视图尺寸的,也应该是由 canvas 的 2dcontext 来获取,这循环实在是很有创意
gouflv
2020-09-30 09:57:45 +08:00
算文字大小可以用 textarea
Chenamy2017
2020-09-30 10:01:58 +08:00
这是 jQuery 程序员写的 vue 吧---哈哈哈,违背了 vue 的设计初衷。
96412hj
2020-09-30 10:04:55 +08:00
@wangxiang #2 你这个会有一个问题,如果加一个展开按钮,放在...后面,层级调高,不太好看
dddddd
2020-09-30 10:05:54 +08:00
做事不带脑子,建议转行
codespots
2020-09-30 10:11:07 +08:00
CSS 能解决的问题,偏要用 JS 解决
xrr2016
2020-09-30 10:14:52 +08:00
上面说的差不多了,还有一种方式是使用 documentFragment,对创建的 fragment 进行操作,完成后一次性用 innerHTML 插入到页面上。
rbq123456
2020-09-30 10:18:05 +08:00
老实说,写 vue 这么久以来,再也没用过 getElementsByClassName 这个东西
hackyuan
2020-09-30 10:23:27 +08:00
vue 表示我不接这个锅
supuwoerc
2020-09-30 10:24:23 +08:00
css 解决,再不济也是{{xxxx.slice(m,n)}}
Torpedo
2020-09-30 10:26:10 +08:00
你这个不就相当于一个组件,改了全局的属性么。。。。

就算用 js,也应该是封装一个...组件,它只改自己就行了。
而且可以所判断,innerText 变才改一次
Hoshinokozo
2020-09-30 10:28:50 +08:00
1.能用 CSS 实现的为啥要用 js 实现?
2.都手动操作 DOM 了那还要 vue 干啥? jQuery 不香吗?
3.都啥年代还用 getElementBy API,querySelector API 不香吗?

综上,统一 13 楼的疑问,这是后端写的 vue 吧?(:doge
Sapp
2020-09-30 10:32:12 +08:00
@diegozhu 在页面做一个隐藏(绝对定位+负 index,不会引起页面重绘)的元素,渲染文本,然后读取这个 dom 的属性,读取的属性也是百分之百准确的,读取出来之后处以 line-height,就是行数,拿到行数用总文字初一下,取个整数,就是每行的文字了,然后减少几个文字(省略号的空间),设置这个 index,再在页面可见元素里渲染 `${msg.slice(0, index)}...`
我记得以前做富本文编辑器就有这种骚操作,不过单独为了个省略号是真的不值得
fengmumu
2020-09-30 11:48:36 +08:00
@diegozhu getComputedStyle 这个可以获取到最后的计算属性
fengmumu
2020-09-30 11:51:41 +08:00
@noe132 要是浏览器尺寸改变的化 这边不触发更新,然后样式已经改变了,就有点尴尬了,如果可以这边还需要绑定一下浏览器窗口尺寸变化事件
fengmumu
2020-09-30 11:54:28 +08:00
@Sapp 可以先判断是不是出现滚动条,然后再取消滚动条展示,直接给覆盖上省略号
flowfire
2020-09-30 11:55:56 +08:00
如果是我的话,可能会直接采取 2 楼所说的,用纯 CSS 实现 禁止换行 + 超出隐藏 + 自动显示三个点。
=======
如果只是想改进源代码,那原来的实现方式是可取的。
为了防止字数过多导致重新渲染次数过多可以加一个补丁。
比如:已知不管使用任何文字,任何字体,任何字间距,只要文字数量超过 X 个,一定会超出范围。
那么第一步就是判断全文是否超过 X 字,超过的部分直接截断。
这样不管字有多长,第一步就先把可能存在的过多的文字直接去掉了。
剩下的部分使用原逻辑执行速度在可容忍范围之内。
whorusq
2020-09-30 13:37:27 +08:00
一般以下两种:
1. 前端 css 处理,考虑样式兼容;
2. 根据字符串长度截断,vue 应该在 计算属性 或 过滤器里 处理
sjhhjx0122
2020-09-30 14:55:33 +08:00
文字超出隐藏,一般前端肯定想到的是 css 实现,一般人真想不到这样实现

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/711713

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX