V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
px920906
V2EX  ›  程序员

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

  •  
  •   px920906 ·
    mdpx · 119 天前 · 4822 次点击
    这是一个创建于 119 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

    真心不喜欢这些需求,直接不许换行多好。
    pigzzz
        44
    pigzzz   118 天前
    css 啊,兄弟,这是基础
    f056917
        45
    f056917   118 天前
    @lixuda 在 flex 元素下面再建个子元素
    wangxiaoaer
        46
    wangxiaoaer   118 天前 via iPhone
    @NullData 同意,如果必须通过计算字符个数,那么无脑遍历显然效率效率太低,二分法可以很大程度加快速度。甚至设置不同的区间用采用不同的分法。
    zouri
        47
    zouri   118 天前
    这段代码我愿称之为 "什么这个特性那个特性,我就一把循环,搞不定就再上一层" 之术
    iyangyuan
        48
    iyangyuan   118 天前 via iPhone
    实在不行高度写死,用 absolute right 0 bottom 0 做一个假的...也比这强吧
    dd0754
        49
    dd0754   118 天前 via iPhone
    这不是前端写的吧,这种情况前端一般用 css
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4702 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 07:05 · PVG 15:05 · LAX 23:05 · JFK 02:05
    ♥ Do have faith in what you're doing.