观摩一段简单粗暴的 vue 代码 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
px920906
V2EX    程序员

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

  •  
      px920906
    mdpx 2020-09-29 23:42:59 +08:00 6408 次点击
    这是一个创建于 1845 天前的主题,其中的信息可能已经有所发展或是发生改变。

    是一个 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 分多钟,上个截图大伙感受一下:

    4.2min

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

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

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

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

    大家有什么好的方案?

    49 条回复    2020-10-01 09:00:25 +08:00
    azh7138m
        1
    azh7138m  
       2020-09-29 23:48:23 +08:00 via iPhone   2
    wangxiang
        2
    wangxiang  
       2020-09-29 23:56:17 +08:00   5
    . textContent {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    }
    NullData
        3
    NullData  
       2020-09-30 00:27:47 +08:00 via iPhone   2
    看了之后有一点未经验证的小想法,可能比较粗糙
    1.查找算法可以换成二分查找来提升性能
    2.每次计算元素高度不用 dom 树中的节点,用 document.createElement 新建一个元素,操作他来避免 dom 树的回流来提升性能。
    9yu
        4
    9yu  
       2020-09-30 00:37:44 +08:00 via Android
    css 实现。
    noe132
        5
    noe132  
       2020-09-30 01:09:15 +08:00
    我用类似思路实现过一个动态的多行带省略号的判断。
    这个计算只在加载时执行一次,不需要每次 update 都重复计算
    加上二分查找,通常不需要太多次就能计算出来
    woncode
        6
    woncode  
       2020-09-30 02:07:28 +08:00 via Android   1
    看不懂用 vue 怎么还要“document.getElementsByClassName”,那不是 jquery 时代的写法,现在的 mvvm 框架不是应该直接在模板去变量绑定,响应式更新吗
    Sapp
        7
    Sapp  
       2020-09-30 02:08:23 +08:00   1
    css 自带这个功能...
    Sapp
        8
    Sapp  
       2020-09-30 02:10:59 +08:00   1
    另外就算不用 css,也不应该这么操作,应该把需要这个操作的文字都封装从一个组件里传入,在这个组件 mounted 之后获取一下高度,然后计算字符数量,从 template 里直接 slice(0,xxx)。计算字符这个操作不需要渲染,直接在内存就能操作
    Sapp
        9
    Sapp  
       2020-09-30 02:14:28 +08:00
    最不济的方式也是直接给外层固定高度的元素设置超出的全部隐藏,然后获取一下里面高度,如果高度超出的话给外边添加一个 class,class 的内容是...,通过 position 定位看起来像结尾的 ...。哪有挨个操作 dom 这个骚操作
    cxe2v
        10
    cxe2v  
       2020-09-30 02:33:02 +08:00
    @Sapp 挨个操作不算骚,这个最骚的是每次循环减掉 4 个字然后看高度达标没,不达标继续循环
    Mutoo
        11
    Mutoo  
       2020-09-30 07:11:19 +08:00
    可以用二分法优化一下,O(logN),:doge:
    KuroNekoFan
        12
    KuroNekoFan  
      nbsp;2020-09-30 07:53:16 +08:00 via iPhone   2
    这种就是很典型的只懂 vue 的前端开发者能干出来的事了……
    akakidz
        13
    akakidz  
       2020-09-30 08:41:54 +08:00
    这是后端写的 Vue 吧 :doge:
    p8YFk4f3E8SJ3aEv
        14
    p8YFk4f3E8SJ3aEv  
       2020-09-30 08:46:50 +08:00
    能用 CSS 实现的为什么要用 JS
    p8YFk4f3E8SJ3aEv
        15
    p8YFk4f3E8SJ3aEv  
       2020-09-30 08:47:36 +08:00
    单行:

    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    多行:

    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
    or:

    overflow:hidden;
    text-overflow:ellipsis;
    display:-webkit-box;
    -webkit-box-orient:vertical;
    -webkit-line-clamp:2;


    PS:兼容 IE 的话要设置宽度
    AV1
        16
    AV1  
       2020-09-30 08:47:44 +08:00
    这是 jQuery 程序员写的 vue 吧 :doge:
    diegozhu
        17
    diegozhu  
       2020-09-30 08:50:46 +08:00
    @Sapp 请教一下,mount 后的组件也不能保证算出来的高度就是实际的高度吧?涉及到 style,起码 font-size,line-height,word-spacing 不一样对 size 影响很大。
    treblex
        18
    treblex  
       2020-09-30 08:56:43 +08:00
    有些使用 css,必需要 js 修改的话,在请求接口之后 赋值到 data 之前操作,
    treblex
        19
    treblex  
       2020-09-30 08:57:01 +08:00
    @suke971219 #18 优先 css
    hbolive
        20
    hbolive  
       2020-09-30 08:57:24 +08:00
    这明显是 CSS 基础不牢。。
    Czzzzzzzzzzr
        21
    Czzzzzzzzzzr  
       2020-09-30 09:09:09 +08:00
    说实话我还从来没有想到过还可以循环来减长度。。
    KuroNekoFan
        22
    KuroNekoFan  
       2020-09-30 09:24:35 +08:00 via iPhone   1
    事实上在某些场景下需要精准获得字符占的视图尺寸的,也应该是由 canvas 的 2dcontext 来获取,这循环实在是很有创意
    gouflv
        23
    gouflv  
       2020-09-30 09:57:45 +08:00 via iPhone
    算文字大小可以用 textarea
    Chenamy2017
        24
    Chenamy2017  
       2020-09-30 10:01:58 +08:00
    这是 jQuery 程序员写的 vue 吧---哈哈哈,违背了 vue 的设计初衷。
    96412hj
        25
    96412hj  
       2020-09-30 10:04:55 +08:00
    @wangxiang #2 你这个会有一个问题,如果加一个展开按钮,放在...后面,层级调高,不太好看
    dddddd
        26
    dddddd  
       2020-09-30 10:05:54 +08:00
    做事不带脑子,建议转行
    codespots
        27
    codespots &nbp;
       2020-09-30 10:11:07 +08:00
    CSS 能解决的问题,偏要用 JS 解决
    xrr2016
        28
    xrr2016  
       2020-09-30 10:14:52 +08:00
    上面说的差不多了,还有一种方式是使用 documentFragment,对创建的 fragment 进行操作,完成后一次性用 innerHTML 插入到页面上。
    rbq123456
        29
    rbq123456  
       2020-09-30 10:18:05 +08:00
    老实说,写 vue 这么久以来,再也没用过 getElementsByClassName 这个东西
    hackyuan
        30
    hackyuan  
       2020-09-30 10:23:27 +08:00   1
    vue 表示我不接这个锅
    supuwoerc
        31
    supuwoerc  
       2020-09-30 10:24:23 +08:00
    css 解决,再不济也是{{xxxx.slice(m,n)}}
    Torpedo
        32
    Torpedo  
       2020-09-30 10:26:10 +08:00
    你这个不就相当于一个组件,改了全局的属性么。。。。

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

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

    真心不喜欢这些需求,直接不许换行多好。
    pigzzz
        44
    pigzzz  
       2020-09-30 16:18:27 +08:00
    css 啊,兄弟,这是基础
    p8YFk4f3E8SJ3aEv
        45
    p8YFk4f3E8SJ3aEv  
       2020-09-30 16:27:06 +08:00
    @lixuda 在 flex 元素下面再建个子元素
    wangxiaoaer
        46
    wangxiaoaer  
       2020-09-30 16:36:57 +08:00 via iPhone
    @NullData 同意,如果必须通过计算字符个数,那么无脑遍历显然效率效率太低,二分法可以很大程度加快速度。甚至设置不同的区间用采用不同的分法。
    zouri
        47
    zouri  
       2020-09-30 17:23:02 +08:00
    这段代码我愿称之为 "什么这个特性那个特性,我就一把循环,搞不定就再上一层" 之术
    iyangyuan
        48
    iyangyuan  
       2020-09-30 22:33:17 +08:00 via iPhone
    实在不行高度写死,用 absolute right 0 bottom 0 做一个假的...也比这强吧
    dd0754
        49
    dd0754  
       2020-10-01 09:00:25 +08:00 via iPhone
    这不是前端写的吧,这种情况前端一般用 css
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1067 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 18:27 PVG 02:27 LAX 11:27 JFK 14:27
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86