Производительность апплета WeChat, реализация проверки сбора данных о поведении

Апплет WeChat

Отличие апплета от обычной веб-разработки

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

Поток рендеринга разработки веб-страницы и поток сценария являются взаимоисключающими, поэтому длительное выполнение сценария может привести к тому, что страница перестанет отвечать на запросы, в то время как в апплете они разделены и выполняются в разных потоках. Веб-разработчики могут использовать DOM API, предоставляемый различными браузерами, для выбора DOM и управления им. Как упоминалось выше, уровень логики и уровень рендеринга апплета разделены.Слой логики работает в JCore и не имеет полного объекта браузера, поэтому в нем отсутствуют соответствующие API DOM и API BOM. Это отличие приводит к тому, что некоторые библиотеки, хорошо знакомые с фронтенд-разработкой, такие как jQuery, Zepto и т. д., не работают в небольших программах. В то же время среда JCore отличается от среды NodeJS, поэтому некоторые пакеты NPM нельзя запускать в небольших программах.

Среда, с которой приходится сталкиваться веб-разработчикам, — это различные браузеры: ПК должен работать с браузерами IE, Chrome, QQ и т. д., а мобильный терминал должен работать с различными браузерами, такими как Safari, Chrome, системы iOS и Android. , Веб-просмотр. В процессе разработки апплета нам приходится сталкиваться с клиентами WeChat двух основных операционных систем, iOS и Android, а также с инструментами разработчика апплета для вспомогательной разработки.Три операционные среды апплета также различаются.

Эксплуатационные ограничения

Из соображений безопасности динамическое выполнение кода JS не поддерживается в мини-программах, а именно:

Выполнение кода JS с помощью eval не поддерживается. Создание функций с новой функцией не поддерживается.

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

Механизм работы мини-программы

запуск апплета

Есть две ситуации, когда апплет запускается: «холодный запуск» и «горячий запуск».

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

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

передний/фоновый статус

Когда пользователь нажимает кнопку капсулы в правом верхнем углу, чтобы закрыть апплет, или нажимает кнопку «Домой» устройства, чтобы выйти из WeChat, апплет не уничтожается напрямую, а переходит в фоновое состояние;

Когда пользователь снова входит в WeChat или снова открывает апплет, апплет снова выходит на передний план из фона.

Мини уничтожение программы

Следует отметить, что только когда апплет переходит в фоновый режим на определенный период времени или использование системных ресурсов слишком велико, он будет действительно уничтожен.

Когда апплет переходит в фоновый режим, клиент некоторое время остается в рабочем состоянии.По истечении определенного периода времени (в настоящее время 5 минут) апплет будет активно уничтожен WeChat. Когда мини-программа занимает слишком много системных ресурсов, она может быть уничтожена системой или активно переработана клиентом WeChat. В iOS, когда клиент WeChat получает два или более последовательных сигнала тревоги системной памяти в течение определенного интервала времени (в настоящее время 5 секунд), он будет активно уничтожать апплет и предлагать пользователю «Этот апплет может вызвать реакцию WeChat. Замедление прекращено». . Рекомендуется, чтобы апплет использовал wx.onMemoryWarning для отслеживания предупреждений о памяти и при необходимости выполнял необходимую очистку памяти.

Механизм обновления мини-программы

Обновить, если не запущен

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

Обновления при запуске

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

Если вам нужно сразу применить последнюю версию, вы можете использовать для этого wx.getUpdateManager API.

Трудности и ключевые моменты разработки мини-программного зонда

  • Невозможно напрямую перехватывать/прослушивать запросы

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

  • Гарантия мониторинга совместимости трех операционных сред

    • На Android среда выполнения JS Runtime - ядро ​​X5
    • В iOS среда выполнения js — это JavaScriptCore.
    • В средстве разработки операционная среда js — nwjs (ядро Chrome).
  • Поведение пользователя не может быть отслежено напрямую

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

  • SDK должен быть легким

    Существует ограничение на размер небольшого пакета программы.Максимальный размер одного пакета 2M.В случае субподряда он не может превышать 8M, поэтому sdk должен быть облегченным.

  • Большой объем сбора данных, минимизация потерь производительности

    Необходимо разработать буферный пул и сформулировать стратегию отчетности.

  • Не влияет на бизнес (основные потребности)

Зондовый буферный пул и политика отчетности

Данные, собранные датчиком, в основном делятся на два типа: основные данные и данные о характеристиках событий. Данные о характеристиках будут упомянуты в следующих ключевых событиях.

Основные данные

Базовые данные — это данные, включенные в каждый журнал отчетов. Некоторые из них получаются после инициализации зонда и не изменяются, эта часть данных настраивается пользователем, имеющим отношение к бизнесу, а остальные данные генерируются внутри зонда или получаются вызовом функции wx.getSystemInfoSync. API

Другая часть, с поведением пользователя, таким как переключение страниц, посадка или изменения среды, например, когда меняется сеть, изменится.

Сетевые данные получают через wx.getnetworktype и wx.onnetworkstatuschange. Часть заголовка упоминается в следующих ключевых событиях

Данные о характеристиках события

Политика эскалации

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

Пока сообщается отчет о данных, кэшированные журналы будут очищаться, чтобы предотвратить накопление кэшированных журналов из-за сбоев отчетов.

Захват ключевых событий зонда

ключевой тип события

Переопределить конфигурацию приложения

Для класса App в основном перепишите жизненные циклы «onShow», «onHide», «onError» и «onLaunch» в конфигурации.функция перехвата кеша Подключите метод к конфигурации, добавьте обратный вызов жизненного цикла по умолчанию для ненастроенного жизненного цикла в конфигурации.Конфиг содержит функции жизненного цикла "onShow", "onHide", "onError", "onLaunch", а функция ловушки вызывается после выполнения исходного метода.

  • стартовое событие (старт)

    Когда апплет запустится, получите значение сцены запуска апплета, перепишите конфиг приложения и запустите его через onLaunch. Получите сцену значения начальной сцены апплета, путь к странице, поиск страницы и получите заголовок страницы через путь к странице и объект __wxConfig.

  • Выход в тыл (пауза)

    Апплет переключается в фоновый режим, перезаписывает конфигурацию приложения и запускает его через onHide.

  • Вернуться на передний план (возобновить)

    Апплет просыпается из фона и возвращает значение сцены переключателя обратно в апплет. Перепишите конфигурацию приложения и запустите его через onShow (кроме первого триггера onShow).

  • ловушка исключения

    Поскольку метод глобального мониторинга апплета wx.onError поддерживается только в версии 2.1.2 и выше, для обеспечения совместимости конфигурация приложения должна быть переписана и активирована onError.

Переопределить конфигурацию страницы

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

  • Остаться на странице (page_stay)

    Запускается, когда onHide и onUnload, получают время, в течение которого пользователь остается на текущей странице.Для сценария, когда onHide запускается при совместном использовании страницы переадресации, отчет о пребывании страницы не выполняется.

  • переключение страниц

    Запускается каждый раз, когда вы переключаете страницы (onShow), получаете текущий путь к странице, параметры, заголовок

  • Время первого рендеринга страницы

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

  • Поделиться страницей (поделиться)

    Инициируется, когда пользователь делится переадресованной страницей, инициируется переписыванием конфигурации страницы, onShareAppMessage. Поскольку совместное использование страницы запускает жизненный цикл onShow, onHide текущей страницы, для точности данных установите переменную isPause для идентификации.

Фиксация поведения пользователя

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

Для компонента и поведения просто подключите метод к его config.method Определите, является ли текущая функция функцией события, по тому, имеет ли формальный параметр атрибут currentTarget.

События кликов, не имеющие атрибутов настраиваемых событий, идентифицируются как события щелчков, а существующие — как настраиваемые события.

  • событие щелчка

    Поскольку уровень логики апплета отделен от уровня рендеринга, уровень логики работает в JCore, отсутствует полный объект браузера, отсутствуют API-интерфейсы, связанные с DOM и BOM, а метод глобального мониторинга событий щелчка не может быть установлен для тела.

    Чтобы реализовать мониторинг событий, зонд различает все атрибуты в конфигурации, переписывая конфигурацию страницы, компонента и поведения, чтобы определить, является ли текущий атрибут функцией, и когда функция запускается, имеет ли формальный параметр атрибут currentTargey. чтобы различать формальный параметр Является ли это объектом события для отслеживания событий страницы.Для событий касания и длительного нажатия зонд идентифицируется как событие щелчка.

    Типы Условия срабатывания
    tap Уходите, как только ваш палец коснется
    longpress После касания пальца оставить его после более чем 350 мс, если указана функция обратного вызова события, и это событие срабатывает, событие касания не будет срабатывать

  • пользовательское событие (журнал)

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

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

    Поскольку источник события, когда событие инициируется, инкапсулируется внутри WeChat, получение пользовательских атрибутов в настоящее время поддерживает получение только в виде атрибута данных data-xxx, поэтому, когда он не вызывается вручную, вы можете добавить data-event и data-log для добавления пользовательского кода события с низким уровнем связи.

Перепишите объект wx для захвата событий API.

  • событие API (API)

    Перезапишите объект wx, перепишите конфигурацию метода wx.request и получите api (адрес интерфейса данных), api_method (метод запроса интерфейса данных), api_status (код состояния ответа интерфейса данных), api_response_time (время ответа интерфейса данных (мс)). ) ), api_response_content_length (длина содержимого ответа интерфейса данных (в байтах))

    API апплета в основном монтируется на глобальный объект wx, и если вы напрямую модифицируете свойства на wx, будет сообщено об ошибке, и прямое присвоение не удастся (у апплета есть ограничения на это)

    thirdScriptError 
     sdk uncaught third Error 
     Cannot set property request of #<Object> which has only a getter 
     TypeError: Cannot set property request of #<Object> which has only a getter
    

    альтернативный план

    Используйте Object.getOwnPropertyDescriptors, чтобы получить дескриптор свойства объекта wx, переназначить объект WeChat пустому объекту, зациклить дескриптор свойства, определить, является ли ключ текущего дескриптора запросом, и преобразовать дескриптор свойства запроса, в противном случае используйте Object. Метод defineProperty определяет свойство

    wx对象属性描述符

    Поскольку цикл for in не может получить ключ типа Symbol, чтобы быть совместимым со сценарием, когда объект wx в будущем будет вводить Symbol в качестве ключа объекта wx, используйте метод Object.getOwnPropertySymbols для получения Символ в дескрипторе свойства, а затем переопределить свойство

    Этот кусок кода слишком много, сделать скриншоты не просто, просто перейдите к коду

    // 重写wx.request
      rewriteWxRequest() {
        const that = this;
        
        // return 
        // 重写wx对象start
        const descriptorObj = Object.getOwnPropertyDescriptors(wx);
        let oldWx = this.oldWx = wx;
        wx = {};
        for (let i in descriptorObj) {
          if (i === 'request') {
            const desObj = descriptorObj[i];
            let oldGet = desObj.get;
            desObj.get = function(...args){
              let oldRequest = oldGet.apply(this, args);
              return function(params){
                const {
                  url,
                  method = 'GET',
                  success = function(){},
          
                } = params;
                // 检查API请求是否在忽略的url中
                const ignoreUrls = that.conf.api_ignore_urls;
                if (url && isIgnoreApi(url, ignoreUrls)) {
                  return oldRequest.call(this, params);
                }
                // 处理自定义 api url trim func
                let apiTrimUrl = null;
                if (that.conf.api_property_cb) {
                  try {
                    apiTrimUrl = that.conf.api_property_cb(url) || null;
                  } catch (e) {
                    apiTrimUrl = null;
                  }
                }
    
                const timeStamp = Date.now();
                const apiData = {
                  api: apiTrimUrl || cutAPIUrl(url),
                  api_method: method.toUpperCase(),
                  api_status: undefined,
                  api_response_time: 0,
                  api_response_content_length: 0,
                }
                return oldRequest.call(this, {
                  ...params,
                  success (res) { // 成功回调
                    try {
                      const {
                        data,
                        statusCode
                      } = res
                      apiData.api_status = statusCode;
                      apiData.api_response_time = Date.now() - timeStamp;
                      if (data) {
                        let AB = {};
                        if(typeof ArrayBuffer !== undefined) {
                          AB = ArrayBuffer;
                        }
                        if (data instanceof AB && data.byteLength !== undefined) {
                          apiData.api_response_content_length = data.byteLength;
                        } else {
                          if (typeof data === 'string') {
                            apiData.api_response_content_length = data.length || 0;
                          } else {
                            apiData.api_response_content_length = JSON.stringify(data).length || 0;
                          }
                        }
                      } else {
                        apiData.api_response_content_length = 0;
                      }
                      that.reportApi(apiData)
                    } catch (e) {
                      that.consoleErr(e);
                    }
                    success.call(this, res);
                  },
                })
              }
            }
            Object.defineProperty(wx, i, desObj)
          } else {
            Object.defineProperty(wx, i, descriptorObj[i])
          }
        }
        // 对微信将来引入Symbol的情况进行兼容,防止丢失以Symbol为键的情况
        if (Object.getOwnPropertySymbols && typeof Object.getOwnPropertySymbols === 'function') {
          Object.getOwnPropertySymbols(descriptorObj).forEach(val => {
            Object.defineProperty(wx, val, descriptorObj[val])
          })
        }
        // 重写wx对象end
      }
    

быть оптимизированным

  • Исключение ошибки не может быть найдено в исходном коде
  • В настоящее время поддерживаются только собственные фреймворки и фреймворки mpvue, сторонние плагины для WeChat неприменимы.
  • Пользовательские события не могут быть добавлены к какой-либо метке, такой как веб-пробы.