«Внешняя оптимизация» — утечка памяти в iview

Vue.js iView
«Внешняя оптимизация» — утечка памяти в iview

предисловие

Недавно менеджер по продукту сообщил мне, что когда одна из систем компании часто переключает главное меню, потребление памяти браузером резко возрастает. В то время первой реакцией была утечка памяти. После воспроизведения сцены обнаруживается, что при переключении из главного меню A в главное меню B и обратном переключении в главное меню A память, занимаемая браузером, удваивается, и в основном можно подтвердить, что это утечка памяти.

1. Процесс расследования

По воспроизведению сцены BUG память, занимаемая браузером, увеличилась вдвое, и можно предварительно определить, что компонент Vue не был освобожден.

В Vue, если компонент Vue имеет утечку памяти, компоненты, которые ссылаются друг на друга, не могут быть уничтожены. Почему это так, можно объяснить ниже?

Первое, что нужно понять, это то, что компоненты Vue в конечном итоге будут отображаться в дереве DOM.Например, компонент таблицы состоит из нескольких компонентов ячеек, если вы сохраните ссылку на ячейку в коде JS. В будущем вы решите удалить таблицу из DOM и сохранить ссылку на ячейку. Вы можете подумать, что сборщик мусора вернет все, кроме этой ячейки, но это не так. Ячейка является дочерним узлом таблицы, и все дочерние узлы сохраняют ссылку на свой родительский узел. Другими словами, ссылки на ячейки в коде JS заставляют всю таблицу храниться в памяти.

Так что используйте функцию «Память» в инструментах разработчика Chrome для проверки, и вы узнаете, почему каждый компонент имеет утечки памяти, что делает невозможным запуск.

В это время нам нужно использовать метод исключения, чтобы найти точку утечки.Я уже говорил, что утечка памяти происходит при переключении главного меню.Сначала аннотировать содержимое каждой страницы меню, а затем переключать главное меню.После тестирования , утечки памяти нет.

Откройте комментарии для каждого компонента на странице главного меню A, переключите тесты главного меню A и B и, к счастью, нашли точку утечки, в компонентах разделения панели Split есть утечка памяти.

Для исключения других влияний был введен отдельный тестовый проект, введена панель Split в iview для разделения компонентов, переключена маршрутизация, обнаружена утечка памяти.

Убедитесь, что iview в проекте имеет версию 3.4.2, и проверьте исходный код на GitHub.

В функции монтируемого хука есть один. Окно привязано к монитору изменения размера, но оно не отвязано в функции хука beforeDestroy. Скопируйте код компонента в тестовый проект и измените его на

methods:{
    handleUp () {
        this.isMoving = false;
        document.removeEventListener('mousemove',this.handleMove,false);
        document.removeEventListener('mouseup',this.handleUp,false);
        this.$emit('on-move-end');
    },
    handleMousedown (e) {
        this.initOffset = this.isHorizontal ? e.pageX : e.pageY;
        this.oldOffset = this.value;
        this.isMoving = true;
        document.addEventListener('mousemove',this.handleMove,false);
        document.addEventListener('mouseup',this.handleUp,false);
        this.$emit('on-move-start');
    },
    computeOffset(){
        this.offset = (this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000 / 100;
    }
},
watch: {
    value () {
        this.computeOffset();
    }
},
mounted () {
    this.$nextTick(() => {
        this.computeOffset();
    });
    window.addEventListener('resize',this.computeOffset);
},
beforeDestroy(){
    window.removeEventListener('resize',this.computeOffset);
}

Перепроверил и обнаружил, что утечка памяти исчезла

Вернемся к исходному проекту, панель iview разделена на отредактированные компоненты замены компонентов, которые были протестированы, утечки памяти не происходит.

2. Об удалении ямы мониторинга событий

Такой код есть в компоненте Split panel split в iview

mounted () {
    this.$nextTick(() => {
        this.computeOffset();
    });
    window.addEventListener('resize', ()=>{
        this.computeOffset();
    });
}

Как удалить этот прослушиватель событий в beforeDestroy

beforeDestroy(){
    window.removeEventListener('resize', ()=>{
        this.computeOffset();
    });
}

или

beforeDestroy(){
    window.removeEventListener('resize',this.computeOffset)
}

После тестирования вы обнаружите, что утечка памяти все еще не устранена, и вам будет очень странно, почему утечка памяти все еще происходит после того, как мониторинг событий был удален.На самом деле, здесь есть яма, и я представлю ее ты ниже.

Если вы хотите удалить прослушиватели событий, необходимо выполнить следующие пункты.

  1. Функция выполнения AddEventListener() должна использовать внешнюю функцию, пример:

window.addEventListener('resize',this.computeOffset); 2. Функция исполнения не может быть анонимной функцией, например:window.addEventListener('resize', ()=>{this.computeOffset()}); 3. Исполнительная функция не может изменить указатель this, например:window.addEventListener('resize',this.computeOffset.bind(this)); 4.addEventListenerа такжеremoveEventListenerТретий параметр должен быть непротиворечивым, например: window.addEventListener('resize',this.computeOffset,true); window.removeEventListener('resize',this.computeOffset,true);

```
window.addEventListener('resize',this.computeOffset,false);
window.removeEventListener('resize',this.computeOffset,false);
```