vinta / pangu.js

Paranoid text spacing in JavaScript
https://chrome.google.com/webstore/detail/paphcfdffjnbcgkokihcdjliihicmbpd
MIT License
4.36k stars 295 forks source link

某些页面会造成卡顿 #101

Closed uclort closed 5 years ago

uclort commented 7 years ago

比如cocoachina论坛,会造成页面的动图卡顿,而且关闭当前标签也会卡住1-3秒,之后才会正常关闭。 Chrome -> MacBook Pro。

woshihuzios123 commented 7 years ago

对,我这边打开 百度贴吧 也会有很严重的卡顿 目测是 加载动画冲突了 示例: https://tieba.baidu.com/p/5302377612 打开插件后再打开这个页面,会看到 底部的加载动画一直显示,但如果关闭插件,那么加载动画就不会一直显示

vinta commented 7 years ago

啊對不起,我程式寫得不好,不過應該是因為用了 DOMSubtreeModified 這個 event,我改天再研究看看有沒有效能好一點的寫法,JavaScript 很難啊。

woshihuzios123 commented 7 years ago

加油

DemoJameson commented 6 years ago

@vinta 看看 https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver 有没有帮助。 我最近发现 B 站的视频使用 H5 播放器时切换全屏会卡顿。 https://www.bilibili.com/video/av17150149/

lqzhgood commented 6 years ago

总归是 Dom 数太多 一次性处理所导致的卡顿。

ShenHongFei commented 5 years ago

感谢 @DemoJameson 和 @lqzhgood 的思路,很有帮助,我按照你们的思路写真的解决了。

  1. 用 Mutation Observer 在 document.body 上按以下配置监听 DOM 结点变化(参考 https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit) attributes: false childList: true subtree: true
  2. 结点变化后执行回调函数,根据传入的 MutationRecord 数组,将新增的结点(mutation.addedNodes) 加入等待空格化的结点队列
  3. 调用经过 debounce 处理的 space_dom 方法,当不再变化 300 ms 之后,开始对每一个队列中的结点执行空格化,执行完后清空结点队列,回到 (2.) 继续等待结点变化。

经过测试以上提到的几个网站均没有性能问题(毕竟只修改了新增的 DOM 结点,而原来脚本里面更新了整个页面。)

以下是一个 UserScript,代码是完整的,debounce 用了 Sugar.js 这个第三方依赖, Webpack 之后即可运行,记得授予 unsafeWindow 权限。

-> # this = unsafeWindow
    debounce = require('sugar/function/debounce')
    unique   = require('sugar/array/unique')

    @CJK= '([\u2e80-\u9fff\uf900-\ufaff])'
    @space= (text)->
        text_ = text
            .replace ///#{CJK}(['"])///g, '$1 $2'
            .replace ///(['"])#{CJK}///g, '$1 $2'
            .replace ///(["']+)\s*(.+?)\s*(["']+)///g, '$1$2$3'

            .replace ///(\S)\#(\S*?)\#(\S)///g, '$1 #$2# $3'
            .replace ///(\S)\#(\S)///g, '$1 #$2'

            .replace ///#{CJK}([\+\-\*\/=&\\\|<>])([A-Za-z0-9])///g, '$1 $2 $3'
            .replace ///([A-Za-z0-9])([\+\-\*\/=&\\\|<>])#{CJK}///g, '$1 $2 $3'

        text_bak = text_
        text_ = text_.replace ///#{CJK}([\(\[\{<\u201c]+(.*?)[\)\]\}>\u201d]+)#{CJK}///g, '$1 $2 $4'

        if text_ == text_bak
            text_ = text_
                .replace ///#{CJK}([\(\[\{<\u201c>])///g, '$1 $2'
                .replace ///([\)\]\}>\u201d<])#{CJK}///g, '$1 $2'

        text_ = text_
            .replace /([\(\[\{<\u201c]+)(\s*)(.+?)(\s*)([\)\]\}>\u201d]+)/, '$1$3$5'
            .replace ///#{CJK}([~!;:,\.\?\u2026])([A-Za-z0-9])///g, '$1$2 $3'
            .replace ///#{CJK}([A-Za-z0-9`\$%\^&\*\-=\+\\\|\/@\u00a1-\u00ff\u2022\u2027\u2150-\u218f])///g, '$1 $2'
            .replace ///([A-Za-z0-9`~\$%\^&\*\-=\+\\\|\/!;:,\.\?\u00a1-\u00ff\u2022\u2026\u2027\u2150-\u218f])#{CJK}///g, '$1 $2'

    @check_node= (node)->
        node = node.parentNode
        while node
            if  node.nodeName.match(/style|script|code|textarea/i) ||
                node.isContentEditable ||
                node.getAttribute?('g_editable') == 'true'
                    return false
            node = node.parentNode
        return true

    @space_dom= (root=document.body)->
        tree_walker = document.createTreeWalker root, NodeFilter.SHOW_TEXT,
            acceptNode: (node)->
                if check_node(node) then return NodeFilter.FILTER_ACCEPT
        while text_node = tree_walker.nextNode()
            text = text_node.data
            text_ = space text
            if text_ != text then text_node.data = text_

    @space_title= ->
        title  = document.title
        title_ = space title
        if title_ != title then document.title = title_

    @mutated_nodes = []
    @space_dom_debounced= debounce ->
            @mutated_nodes = unique @mutated_nodes
            for node in @mutated_nodes then space_dom(node)
            @mutated_nodes = []
        , 300

    @watch_page_to_space= =>
        @observer = new MutationObserver (mutations, observer)->
            for mutation in mutations
                for node in mutation.addedNodes
                    mutated_nodes.push node
            space_dom_debounced()
        @observer.observe document.body,
            attributes: false
            childList: true
            subtree: true

    document.addEventListener 'readystatechange', (event)->
        if document.readyState == 'interactive'
            space_title()
            space_dom()

        if document.readyState == 'complete'
            watch_page_to_space()

.call unsafeWindow

Bug: IT 之家 页面内加载评论后 go_node_spacing 函数无限被调用导致 CPU 占用 100% #116 这个我关掉了。 最后感谢 repo 主 @vinta

vinta commented 5 years ago

幹得好各位!最近終於有空也要來更新一下了,偉哉 debounce()