Принцип рендеринга мини-программы
Для рендеринга интерфейса в двухпотоковом режиме уровень логики и уровень рендеринга апплета представляют собой два отдельных потока. На уровне рендеринга среда хоста будетWXML
преобразован в соответствующийJS
Объект, когда данные изменяются на логическом уровне, нам нужно предоставить данные, предоставленные хост-средой.setData
Метод передает данные с логического уровня на уровень рендеринга, а затем сравнивает разницу до и после, применяет разницу к исходному дереву Dom и отображает правильный интерфейс пользовательского интерфейса.
Таким образом получить: по времени инициализации страницы существенноВремя передачи исходных данных страницыа такженачальное время рендерингаСостоит из двух частей. Среди них время передачи данных относится ко времени с момента, когда данные организованы на логическом уровне, до момента, когда данные полностью получены на уровне представления.Время передачи обычно положительно коррелирует с объемом данных, и передача чрезмерно больших данных значительно увеличит это время.Следовательно, уменьшение объема передаваемых данных является эффективным способом сокращения времени передачи данных..
Ключевые факторы, влияющие на производительность
-
ходить часто
setData
Если вы ходите очень часто (миллисекунды)
setData
, что приводит к двум последствиям:- Под Android пользователи будут чувствовать себя застрявшими при скольжении, а обратная связь по операции серьезно задерживается, потому что
JS
Поток компилировался, выполнялся и отображался, не доставляя события пользовательских операций на логический уровень вовремя, и логический уровень не мог вовремя доставить результаты обработки операции на уровень представления; - Рендеринг задерживается из-за
WebView
изJS
Поток всегда находится в состоянии занятости, время связи между логическим уровнем и уровнем страницы увеличивается, а сообщение данных, полученное уровнем представления, было отправлено несколько сотен миллисекунд, а результат рендеринга не в реальном времени;
- Под Android пользователи будут чувствовать себя застрявшими при скольжении, а обратная связь по операции серьезно задерживается, потому что
-
каждый раз
setData
оба передают много новых данныхпо
setData
Базовая реализация , мы знаем, что наша передача данных на самом делеevaluateJavascript
Процесс скрипта, когда объем данных слишком велик, время компиляции и выполнения скрипта будет увеличиваться, занимаяWebView JS
нить. -
фоновая страница
setData
Когда страница переходит в фоновое состояние (не видна пользователю), она не должна продолжаться.
setData
, отображение страницы фонового состояния пользователем не ощущается. Кроме того, страница фонового состояния переходит наsetData
Также вытесняет выполнение страницы переднего плана. -
существует
data
Поместите много данных, не имеющих отношения к рендерингу интерфейса, в
Оптимизация
1. Уменьшитьsetdata
количество данных
Если данные не влияют на слой рендеринга, их не нужно размещать вsetData
в
2. Объединитьsetdata
просьбы сократить количество сообщений
Избегайте слишком частых звонковsetData
, следует учитывать, что несколькоsetData
сливаться в одинsetData
передача
// 不要频繁调用setData
this.setData({ a: 1 })
this.setData({ b: 2 })
// 绝大多数时候可优化为
this.setData({ a: 1, b: 2 })
3. Удалите ненужные привязки событий (WXML
серединаbind
а такжеcatch
), тем самым уменьшая объем данных и количество сообщений
4. Избегайтеdata
Предотвращение слишком больших данных в атрибуте префикса
5. Частичное обновление списка
В списке естьn
Если вы хотите понравиться определенному фрагменту данных в это время, вы можете увидеть эффект лайка во времени.
- может быть использован
setData
Глобальное обновление, после завершения лайка данные перевыгружаются, и снова выполняется глобальный повторный рендеринг.Преимущества этого: удобно и быстро! Недостатком является то, что пользовательский опыт крайне плохой: после того, как пользователь смахнул более 100 фрагментов данных, при повторном рендеринге будет пустой период.
- Вы также можете использовать частичное обновление, которое будет понравиться
id
Передайте его в прошлом, узнайте, какие данные были нажаты, снова получите данные и найдите соответствующие данные.id
Нижний индекс этого фрагмента данных (index
не изменится), используйтеsetData
Выполните локальное обновление, которое может значительно повысить скорость рендеринга.
this.setData({
list[index]=newList[index]
})
6. Данные, не связанные с отрисовкой интерфейса, не должны размещаться вdata
, рассмотрите возможность установкиpage
под другими полями объекта
Page({
onShow: function() {
// 不要设置不在界面渲染时使用的数据,并将界面无关的数据放在data外
this.setData({
myData: {
a: '这个字符串在WXML中用到了',
b: '这个字符串未在WXML中用到,而且它很长…………………………'
}
})
// 可以优化为
this.setData({
'myData.a': '这个字符串在WXML中用到了'
})
this._myData = {
b: '这个字符串未在WXML中用到,而且它很长…………………………'
}
}
})
7. Запретить фоновую страницуjs
Захватить ресурсы
В апплете может быть n страниц, каждая из которых имеетwebview
(слой рендеринга), но делитесь одним и тем жеjs
рабочая среда. То есть, когда вы переходите на другую страницу (при условии, что это страница B), таймер этой страницы (при условии, что это страница A) и т. д.js
Операция все еще выполняется, она не будет уничтожена и займет ресурсы страницы B.
8. Используйте с осторожностьюonPageScroll
pageScoll
Событие, которое также является коммуникацией,webview
послойныйjs
Общение на логическом уровне. Накладные расходы на связь на этот раз относительно велики, учитывая, что это событие часто вызывается, если функция обратного вызова имеет сложнуюsetData
производительность будет плохой.
9. Используйте компоненты апплета, когда это возможно
Обновление пользовательского компонента выполняется только внутри компонента и не зависит от другого содержимого страницы, и каждый компонент также будет иметь свое независимое логическое пространство. Каждый компонент имеет свои собственные независимые данные,setData
передача.
анализ случая
Контрольно-пропускной пункт: вы часто ходитеsetData
Результат проверки: нет
Контрольная точка: каждый разsetData
оба передают много новых данных
Результат проверки: нет
Контрольная точка: ход страницы фонового состоянияsetData
Результат проверки: существует
Причина: Поскольку операция изменения находится на странице поиска, после того, как пользователь щелкнет, она немедленно вернется на предыдущую страницу для отображения данных, поэтому она выполняется на странице фонового состояния.setData
, чтобы улучшить скорость перехода по страницам.
Код проблемы:
// pages/line/searchResult/searchResult.js
showSearchDetail(e){
...省略代码
let prevpage = this.getPrevPage()
prevpage.setData({
isInSearch: true,
showResult,
keyWord:res.routeName
})
}
КПП: вdata
Поместите много данных, не имеющих отношения к рендерингу интерфейса, в
Результат проверки: существует
Причина: Из-за запрошенного в настоящее время интерфейса для запроса информации о линии.GET/api/Route/List/{cityid}/{pagesize}/{pageno}
Пейджинговые запросы не поддерживаются, и все данные будут возвращены за один раз, поэтому в предыдущем решении, чтобы уменьшить сетевой трафик, генерируемый запросом, все данные будут временно храниться в массиве страниц за один раз, ( массив хранит около 600 или более объектов), а затем отображать некоторые данные по мере необходимости.
Код проблемы:
getLinesInformation(cityID) {
return new Promise((resolve, reject) => {
smartProxy.getRequest(`/Route/List/${cityID}/10/0`)
.then(res => {
this.data.lineArray = res
if (this.data.lineArray)
resolve()
else reject()
})
})
},
решение:
-
Способ 1: использовать трафик для повышения производительности Вместо того, чтобы временно сохранять информацию обо всех линиях, повторяйте запрос каждый раз, когда происходит пейджинговый запрос.
api
, массив массивов, необходимых для обновления отображения страницы.Недостаток: повторные запросы
api
Получайте те же данные, тратьте трафикЭффект оптимизации:
Занимает много времени при переходе на страницу поиска в первый раз
500ms
, а то каждый раз переход на страницу поиска будет занимать время90ms
, средняя загрузка раскрывающегося списка страниц400ms
одна страница -
Способ 2: улучшить схему хранения когда запрос переходит в строку
api
После того, как возвращенные данные не помещаются вdata
поле, вместо этого установитеpage
Остальные поля объекта сохраняются.Преимущества: уменьшить нагрузку на страницу, оптимизировать производительность
Код:
getLinesInformation(cityID) { return new Promise((resolve, reject) => { smartProxy.getRequest(`/Route/List/${cityID}/10/0`) .then(res => { //用lineArray字段存储请求得来的数据 this.lineArray = res if (this.lineArray) resolve() else reject() }) }) },
Контрольная точка: использование не по назначениюonPageScroll
Результат проверки: существует
По причинам: он предназначен для возврата пользователя к последней позиции просмотра, когда пользователь возвращает строку для отображения домашней страницы после просмотра результатов поиска строки.onPageScroll
Событие получает высоту каченияScrollTop
, затем сохраните. но если прошлоonPageScoll
Если событие получено, это эквивалентно срабатыванию хранилища каждый раз, когда выполняется микширование, что серьезно влияет на эффект страницы.
Код проблемы:
onPageScroll: function (e) {
// 页面滚动时执行
// console.log(e);
if (e.scrollTop != 0 && !this.data.isInSearch && !this.data.keyWord) {
//设置缓存
wx.setStorage({
key: 'lineSearchScrollTop',
// 缓存滑动的距离,和当前页面的id
data: e.scrollTop
})
}
},
решение:
- непосредственно через
wx.createSelectorQuery().selectViewport().scrollOffset
Получите высоту полосы прокрутки и вызывайте ее только тогда, когда пользователь щелкает поле поиска, чтобы перейти на страницу поиска, уменьшаяonPageScroll
Влияние событий на производительность страницы
//获取滚动条高度
getScrollTop () {
let that = this
return new Promise((resolve, rej) => {
wx.createSelectorQuery().selectViewport().scrollOffset(function (res) {
that.setData({
scrollTop: res.scrollTop
})
resolve()
}).exec()
})
}
Жизненный цикл страницы
Перед определением показателей производительности необходимо разобраться с жизненным циклом страницы апплета.
Зарегистрируйте функцию на каждой страницеPage()
Среди параметров есть методы жизненного цикла:onLoad
,onShow
,onReady
,onHide
,onUnload
.
Первый обратный вызов жизненного цикла, инициированный страницей,onLoad
, срабатывает при загрузке страницы, параметр является параметром запроса страницы, только один раз для страницы;
с последующимonShow
, следить за отображением страницы иonLoad
Другое дело, если страница скрыта, а затем снова отображается (например: возврат на следующую страницу), этот жизненный цикл также будет запущен;
вызыватьonShow
После этого уровень логики отправит данные инициализации слою рендеринга, и после того, как слой рендеринга завершит первый рендеринг, он уведомит уровень логики о необходимости запуска.onReady
Жизненный цикл, страница только один раз;
onHide
Он срабатывает, когда страница скрыта, но не выгружена, напримерwx.navigateTo
Или нижняя вкладка переключается на другие страницы, апплет переключается на фон и т.д.
onUnload
срабатывает, когда страница выгружается, напримерwx.redirectTo
илиwx.navigateBack
на другие страницы.
Весь цикл
Когда страница открыта
Во-первых, предыдущая страница скрыта, а компоненты новой страницы необходимо инициализировать перед загрузкой следующей страницы. После первого рендеринга страницы компонентready
, последним триггером является страницаonReady
,Как показано ниже:
Порядок жизненного цикла при открытии страницы B со страницы A
При выходе со страницы
При выходе с текущей страницы сначала вызвать выгрузку текущей страницыonUnload
, после чего компонент покидает дерево узловdetached
. Наконец отобразить предыдущую страницу, вызватьonShow
. Как показано ниже:
Порядок жизненного цикла от страницы B обратно к странице A
переключиться на фон
При переходе в фоновый режим апплет и страница не удаляются, а только вызывают скрытие. сначала активировать страницуonHide
, а затем приложениеonHide
. Как показано ниже:
Порядок жизненного цикла при переходе в фоновый режим
переключиться на передний план
При переключении в фоновый режим первым сработает апплетonShow
, то страницаonShow
. Как показано ниже:
Порядок жизненного цикла при переходе на передний план
ключевые показатели эффективности
Поняв жизненный цикл каждого этапа апплета, мы можем сформулировать показатели производительности ключевых узлов, которые расположены в следующей таблице:
Запись данных
Если мы запишем данные интерактивного времени до и после оптимизации каждой страницы и сравним их, мы сможем проанализировать, насколько улучшилась производительность каждой страницы, чтобы определить, делаем ли мы бесполезную работу.
Из приведенных выше ключевых показателей эффективности извлеките время интерактивного взаимодействия как один из важных показателей оценки на этот раз, то есть со страницы апплета.
onload
После подсчета события страница инициирует асинхронный запрос, а после возврата запроса данные передаются черезsetData
После рендеринга на страницу время, потраченное на весь процесс выше.
Однако проект апплета часто имеет много страниц, и вручную записывать время первого экрана каждого апплета очень проблематично.
Следовательно, мы можем переписатьthis.setData
метод, добавляя логику отчетной точки времени.
this._startTime = new Date().getTime();
let fn = this.setData;
this.setData = (obj = {}, handle = '') => {
let now = new Date().getTime();
// 上报渲染所需要的时间
log(now - this._startTime)
fn.apply(this, [obj, handle]);
};
Кроме того, есть еще некоторые показатели производительности, которые необходимо записывать.На этой странице отображения маршрута челночного маршрута время загрузки страницы, когда пользователь перемещается вниз, и время от щелчка в поле поиска до момента, когда страница поиска На этот раз наши важные показатели также загружены Критерии оценки. Для этого пользовательского сценария мы можем использоватьconsole.time()
а такжеconsole.timeEnd()
Эта пара функций для записи.
Тест индикатора
Тестовая платформа: Xiaomi Mi 8 SE, мини-инструмент для разработки программ
Процесс тестирования: Главная страница -> Строка -> Выпадающий список вниз -> Нажмите на поле поиска
Индикаторы теста: интерактивное время, время загрузки страницы, время перехода на страницу
Метрики после оптимизации:
Платформа | Время взаимодействия (мс) | Время загрузки страницы (мс) | Время перехода на страницу (мс) |
---|---|---|---|
Инструменты разработки мини-программ | 400 | 130 | 180 |
Xiaomi Mi 8 SE (режим отладки реального компьютера сканирования QR-кода) | 3000 | 110 | 1000 |
Среди них, из-за собственных проблем в режиме отладки реального компьютера при сканировании QR-кода, это нормально, что время увеличивается.В режиме автоматического режима отладки реального компьютера различные индикаторы возвращаются к норме, но поскольку точных данных нет, он не указан в таблице.
Опыт оптимизации
С тех пор выполнение оптимизации строки отображается оптимизация страницы завершена, фактический процесс оптимизации, обнаруживающий, что влияет на производительность самых больших проблем, является следующим
- Выпадающие нагрузки больше, особенно для специальных карт. Сначала я подумал, что это связано с тем, что все больше и больше данных сохраняется на выпадающей странице. После уменьшения данных, хранящихся в экземпляре страницы, было обнаружено, что производительность не сильно улучшилось. Позже выяснилось, что из-за необходимости отслеживать событие прокрутки, событие прокрутки срабатывало часто, а в функции обратного вызова выполнялась трудоемкая операция, в результате которой
onreachBottom
Событие блокируется, то есть инициирование запроса следующей страницы занимает около 1-2 секунд. отменитьscroll
Производительность мониторинга событий значительно улучшена. В конечном счете, это все же для небольших программapi
Незнакомый, частый мониторинг, чтобы получить высоту полосы прокруткиscroll
Инциденты можно охарактеризовать как постановку телеги впереди лошади.