Давайте сделаем небольшую программу жить вместе!

JavaScript

Прямая трансляция была очень популярна в последние два года, и все больше и больше компаний проводят прямые трансляции.Особенно в этом году оптимизация небольших программ для компонентов прямой трансляции (оптимизация производительности + рендеринг на экране) породила множество мелких программировать приложения для прямых трансляций. Мини-программа Jingxi Live находится в сети уже несколько месяцев, поэтому, основываясь на внутреннем обмене информацией, я приведу здесь несколько ключевых сцен прямой трансляции.

Скан-код WeChat, добро пожаловать:
京喜直播

Этот каталог статей:

  • Обработка сообщений в живом зале
  • Скольжение вверх и вниз в гостиной
  • Связь между компонентами
  • Холст все в одном

Обработка сообщений в живом зале

По сравнению с традиционными средствами массовой информации основная особенность прямой трансляции заключается в том, что она более интерактивна, поэтому генерируется большое количество интерактивных действий, таких как лайки, заказы, купоны и другие сообщения, которые необходимо транслировать, поэтому большое количество сообщений контент генерируется, поэтому для них Как мы обрабатываем сообщение?

первый метод

Проще всего отобразить на странице после получения сообщения напрямую Код выглядит следующим образом:

function receiveMessage(msg){
    this.data.msgList.push(msg);
    this.setData({
        msgList:this.data.msgList
    });
}

Этот метод прост, но когда сообщений становится все больше и больше, массив будет становиться все больше и больше, количество узлов будет становиться все больше и больше, и комната прямой трансляции в конечном итоге лопнет.

Второй способ

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

function receiveMessage(msg){
    if(this.data.msgList.length>30){
        this.data.msgList.shift();
    }
    this.data.msgList.push(msg);
    this.setData({
        msgList:this.data.msgList
    });
}

Как и выше, узлы сообщений на нашей странице будут контролироваться в пределах 30. Конечно, есть еще один очень важный момент, который необходимо принять.«Обновление на месте»стратегия.

Стратегия обновления на месте: при изменении содержимого нет необходимости заново создавать узел DOM, а необходимо предоставить общий доступ к существующему узлу DOM, а затем обновить в нем содержимое, и производительность значительно повышается. Узел сообщений не увеличивается в размерах со временем, и это не проблема для большинства сценариев.

третий метод

Как и во втором методе выше, после более чем 30 узлов один будет добавлен, а другой будет удален, и будет принята стратегия обновления на месте, что, похоже, не проблема. Однако в некоторых очень популярных случаях мы все же обнаружили проблему карты сравнения страниц. Причина в том, что производительность сдвига не очень хорошая.Когда количество сообщений слишком быстрое, количество раз для выполнения сдвига будет очень частым, то есть msgList часто изменяется, и в результате страница выполнял сдвиг и толчок. , число рендерингов объекта Dom очень частое, а производительность сильно снижается.

Во-первых, подумайте о сокращении операций или переключении на более производительную реализацию. Итак, подумайте о срезе, сращивании, давайте посмотрим на производительность этих трех.

Сравнение производительности нарезки, сращивания и сдвига

Во-первых, мы используем бенчмарк для тестирования, тестовый код выглядит следующим образом:

var Benchmark = require('benchmark');
var suite = new Benchmark.Suite();
const MAX_COUNT = 100000;
suite
  .add("shift#test", function () {
    var list = [****];// 预设 30 个节点
    let i = 1;
    while (i < MAX_COUNT) {
      list.shift();
      list.push({ a: i, b: "b" });
      i++;
    }
  })
  .add("slice#test", function () {
    var list = [****];// 预设 30 个节点
    let i = 1;
    while (i < MAX_COUNT) {
      list = list.slice(1);
      list.push({ a: i, b: "b" });
      i++;
    }
  })
  .add("splice#test", function () {
    var list = [****];// 预设 30 个节点
    let i = 1;
    while (i < MAX_COUNT) {
      list.splice(0, 1);
      list.push({ a: i, b: "b" });
      i++;
    }
  })
  ……
  .on('cycle', function(event) {
    console.log(String(event.target));
  })
  .run({ async: true });

Данные теста следующие:

  • shift#test x 207 ops/sec ±6.88% (78 runs sampled)
  • slice#test x 114 ops/sec ±1.89% (71 runs sampled)
  • splice#test x 80.90 ops/sec ±0.71% (67 runs sampled)

207 операций в секунду : выполнение тестового кода 207 раз в секунду.

±6,88% (выбрано 78 запусков): Статистическая погрешность для указанных выше операций в секунду составляет 6,88% для 78 выбранных запусков.

С точки зрения данных, порядок ранжирования производительности, который можно получить здесь:

смещение > срез > соединение

Кажется, что сдвиг, который мы выбрали ранее, по-прежнему самый производительный, конечно, мы можем найти разницу между тремя из спецификации ECMAScript, и действительно сдвиг будет быстрее.

Ссылаясь на смену документации ecma262:Смена работы 39. Умер от голода/код ЕС 262/#цвет…
Ссылаясь на фрагмент документации ecma262:Смена работы 39. Умер от голода/код ЕС 262/#цвет…
Ссылаясь на сплайс документации ecma262:Смена работы 39. Умер от голода/код ЕС 262/#цвет…

Сравните еще раз

Глядя на документацию ecma, мы можем легко выяснить, почему сдвиг работает быстрее, их общая реализация выглядит следующим образом:

  • сдвиг: сделать круговой сдвиг

  • сращивание: необходимо создать новый массив, для сохранения удаленных данных используется локальный цикл, а затем необходимо выполнить циклический сдвиг

  • slice: каждая операция требует создания нового массива, а затем перемещения данных, которые необходимо сохранить, в новый массив.

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

Хотя срез и сращивание имеют дополнительные накладные расходы в дополнение к одному циклу, мы можем уменьшить частоту выполнения массива, накапливая часть данных, а затем равномерно удаляя ее.

Измените тестовый код следующим образом:

.add("slice#test", function () {
    var list = [****];// 预设 30 个节点
    let i = 1;
    while (i < MAX_COUNT) {
      if (i % 20 == 0) {
          list = list.slice(20);
        }
      list.push({ a: i, b: "b" });
      i++;
    }
  })
  .add("splice#test", function () {
    var list = [****];// 预设 30 个节点
    let i = 1;
    while (i < MAX_COUNT) {
        if (i % 20 == 0) {
            list.splice(0, 20);
        }
      list.push({ a: i, b: "b" });
      i++;
    }
  })

Итак, давайте снова протестируем его с помощью бенчмарка, и результаты будут следующими:

shift#test x 210 ops/sec ±7.33% (81 runs sampled) slice#test x 972 ops/sec ±1.69% (86 runs sampled) splice#test x 696 ops/sec ±0.38% (89 runs sampled)

Из этих данных мы видим, что:

срез > сращивание > сдвиг

Поэтому лучше всего использовать slice для обновления данных msgList.

Почему этот фрагмент самый быстрый? Если вы сомневаетесь, попробуйте еще раз прочитать документацию ecma, чтобы оценить разницу в исполнении.

Скольжение вверх и вниз в гостиной

Давайте сначала посмотрим на эффект, нажмите и удерживайте QR-код апплета, щелкните один из них в списке, чтобы войти в комнату прямой трансляции, а затем проведите пальцем вверх для просмотра.

京喜直播
Кёнхи в прямом эфире

Как вы ходите вверх и вниз в комнате прямой трансляции мини-программы?

relative + animation

Это самая распространенная практика в H5.Так делается вверх-вниз комнаты прямой трансляции Jingxi H5, но, к сожалению, в апплете это не работает, потому что слишком... застряло.

scroll-view

Слайд-вид прокрутки свободно, мы можем быть довольным элементом студии A (высота: 100ВХ).

Мы попробовали этот способ, но он все равно принес несколько проблем:

  1. Постраничное перелистывание прокрутки слишком чувствительно, а также имеет сильную инерцию, пользователю трудно дать опыт постраничного перелистывания (эффект динамического управления элементом в нем тоже нехороший) ).
  2. При скольжении на высоту, не являющуюся целым числом (100vh), требуется калибровка и возвращение в исходное положение, а отклик все еще слишком медленный.

swiper

После сравнения все еще необходимо использовать swiper для перемещения вверх и вниз в комнате прямой трансляции. Я сделал это в феврале 2019 года.Этот метод используется, когда видео апплета скользит вверх и вниз., я не ожидал, что год спустя это все еще Kakaka (но производительность все еще значительно улучшилась~).

Во-первых, давайте посмотрим на макет:

<swiper class="scroll-wrap" circular="{{circular}}" vertical="true" duration="300" bindchange="itemChange" bindanimationfinish="animFinish">
    <swiper-item wx:for="{{list}}" wx:key="item" class="scroll-item">
        <image class="item-image"  src="{{item}}" wx:if="{{index>=currentIndex-1&&index<=currentIndex+1}}"/>
    </swiper-item>
    <view class="scroll-content" animation="{{animData}}">
        ……        
    </view>
    </swiper>

Здесь он обёрнут swiper, который рендерит swiper-item по списку прямых трансляций, который нужно отобразить. Что очень приятно, так это то, что когда мы слушаем жест пользователя, чтобы провести пальцем по экрану, а затем запускаем анимацию прокрутки содержимого в реальном времени, она не застревает. Конечно, это также связано с тем, что текущая страница имеет только один прокруточный контент.

Scroll-контент — это все содержимое прямой трансляции страницы. Узел очень большой. После инициализации scroll-контента он всегда будет существовать на странице. содержимое внутри.

Связь между компонентами

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

Связь реквизитов и triggerEvent между свойствами

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

  • Достоинства: Просто и быстро, понятно.
  • Недостаток: все коммуникации должны проходить через уровень представления, что влияет на производительность.

Почему вы говорите, что этот распространенный способ влияет на производительность?

Ниже приводится выдержка из документации по мини-программе:

Уровень представления апплета в настоящее время использует WebView в качестве носителя рендеринга, а уровень логики использует независимый JavascriptCore в качестве рабочей среды. Архитектурно и WebView, и JavascriptCore являются независимыми модулями и не имеют каналов для прямого обмена данными. В настоящее время передача данных между уровнем представления и уровнем логики фактически реализована с помощью EvaluateJavascript, предоставляемого с обеих сторон. То есть данные, передаваемые пользователем, необходимо преобразовать в строку для передачи, и в то же время преобразованное содержимое данных встраивается в JS-скрипт, а затем передается в независимые среды с обеих сторон путем выполнения JS-скрипт.

Из этого отрывка делаем вывод, что:

  1. Производительность взаимодействия между уровнем логики (js) и уровнем представления (wxml) не так хороша, не обновляйте слишком часто.
  2. Логический слой взаимодействия (JS) и слой просмотра (WXML) также нуждаются в EvalueseJavaRcript, достигаемый необходимостью преобразования + обратного компиляции, не обновляйте контент слишком много.

Поэтому, если нам нужно разработать высокопроизводительные небольшие программы, мы должны думать об этом.

отправка и подписка в режиме публикации

Это также простой метод, который широко используется, режим публикации по подписке, который мы также часто используем в H5.

  • Преимущества: простота понимания, простота использования, коммуникация не ограничивается отношениями между компонентами.
  • Недостатки: он не имеет состояния и может иметь ошибки.

Почему может быть ошибка?

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

订阅发布模式
ПОДПИСАТЬСЯ РЕЖИМ ПУБЛИКАЦИИ

Как показано в пути доступа на приведенном выше рисунке, если код страницы B публикует уведомление о сообщении, то две кэшированные в данный момент страницы B получат сообщение и выполнят код в ответ. Таким образом, данные двух страниц B будут загрязнять друг друга.

Основная причина приведенной выше строки сообщения заключается в том, что getAPP().events является уведомлением о сообщении на уровне апплета, а канал сообщений не имеет состояния, хотя он может быть загрязнен.

Конечно, решить эту проблему тоже можно, мы можем решить ее со следующих двух аспектов:

  • Привязка событий к экземпляру уровня страницы будет привязана только к текущему экземпляру страницы.
  • Добавьте область к событиям, таким как внедрение экземпляра текущей страницы, только слушатель на текущем экземпляре страницы получит сообщение.

selectКомпонентная связь

Апплет предоставляет метод selectComponent для получения экземпляра дочернего компонента, поэтому наш переход между родительским и дочерним компонентами очень прост.

  • Достоинства: хорошая производительность, прямой метод исполнения
  • Недостатки: только компоненты «родитель-потомок», а не компоненты «родитель-потомок».
selectComponent
selectComponent

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

При выполнении this.selectComponent требуется время, но нам нужно только кэшировать полученный экземпляр.

В настоящее время комната прямых трансляций Jingxi в основном использует selectComponent для реализации связи между компонентами.

Определите метод corssComponent для реализации межкомпонентной связи, а затем вызовите его напрямую.

export function crossComponent ({ id, fun, params }) {
    if (!id || !fun) return;
    const key = id + '_com';
    if (!this[key]) {
        this[key] = this.selectComponent('#' + id);
    }
    if (this[key] && typeof this[key][fun] == 'function') {
        return this[key][fun](params);
    }
    // 兜底,拿不到实例的时候,再重复拿实例,保存起来
    const self = this;
    (function _t (c) {
        if (c >= 10) return;
        const com = self.selectComponent('#' + id);
        if (com) {
            self[key] = com;
            com && typeof com[fun] == 'function' && com[fun](params);
            return;
        }
        setTimeout(() => {
            _t(++c);
        }, 200)
    })(0);
}
this.crossComponent({id: 'b组件',fun:'add',params:{count:1}});

Только что мы упомянули, что selectComponent не поддерживает прямую связь между компонентами-потомками, так что, если нам нужна связь между компонентами-потомками?

На самом деле нетрудно подумать, что нам нужно только предоставить метод сохранения crossComponent компонентов-потомков.Мы можем искать экземпляры от потомков и внуков, например, id: "B__b2" для представления подкомпонента b2 компонента компонент B и найдите экземпляр b2.subcomponent компонента B, снова выберите Component, чтобы найти b2, а затем сохраните все экземпляры, чтобы вы могли полностью обмениваться данными между компонентами.

Холст все в одном

Canvas такой полезный, H5 такой же, как и апплет.

  • Любимый холст: очень мощный и широко используемый
  • Отвратительный холст: жена... потребляет энергию

В эпоху апплета Canvas3d производительность стала еще более узким местом. Конечно, Canvas2d, рекомендованный апплетом, значительно улучшился с точки зрения производительности, но что, если на странице есть несколько мест, где необходимо использовать Canvas?

К сожалению, в программе Live Mini есть две функции, требующие использования Canvas: отрисовка общих картинок и тому подобное.

В первые дни мы инициализировали два холста прямо на странице, а затем обнаружили, что они сильно зависают на мобильных телефонах с малым объемом памяти и даже выходят напрямую.

Чтобы решить эту проблему, вы можете фактически создать только один узел Canvas на странице и инициализировать Canvas один раз, чтобы решить эту проблему.

Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat, давайте вместе будем надежным интерфейсом!

follow-me