Jquery Mobile + Phonegap 提升 listview 性能
Posted
技术标签:
【中文标题】Jquery Mobile + Phonegap 提升 listview 性能【英文标题】:Jquery Mobile + Phonegap improve listview performance 【发布时间】:2015-02-26 11:16:29 【问题描述】:在我的 JQM 1.4 + Phonegap 3.6 应用程序中,我使用 listview,如下代码所示。
html:
<div id="boardselection">
<ul id="modelsListview" data-role="listview" data-icon="false">
</ul>
</div>
JS:
function resetModelsListView(prodata, firsttime, funfeatureOn, specificBrand, specificPro)
console.log("on passe dans resetModelsListView");
// funfeatureOn = 0;
//debug timer
var time = [];
var dummy;
var i;
var listviewdeferred = $.Deferred();
var optionspro = '';
var optionsbrand = '';
var optionsmodel = '';
var countpros = 0;
var countbrands = 0;
var countmodels = 0;
var chosenmodelListViewHandle = $('#modelsListview');
var chosenbrandSelect = $('#chosenbrand');
optionsmodel += '';
var alreadyusedbrands = [];
prodata.sort(SortByName);
// get previously selected model to reselect it later
//var previouslySelectedModelId =parseInt(chosenmodelSelect.find('li:selected').val());
if (!funfeatureOn)
prodata.sort(SortByModel);
else
prodata.sort(SortByFUN);
//populate model list
//~ if (firsttime)
//~ var perfIsChecked = true;
//~ var smallwaveIsChecked = true;
//~ var stepupIsChecked = true;
//~ else
var perfIsChecked = $('#checkboxperf').is(":checked");
var smallwaveIsChecked = $('#checkboxsmallwave').is(":checked");
var stepupIsChecked = $('#checkboxstepup').is(":checked");
//~
console.log("perfIsChecked, smallwaveIsChecked, stepupIsChecked =");
console.log(perfIsChecked);
console.log(smallwaveIsChecked);
console.log(stepupIsChecked);
//if none checked then no filter
if (!perfIsChecked && !smallwaveIsChecked && !stepupIsChecked)
perfIsChecked = true;
smallwaveIsChecked = true;
stepupIsChecked = true;
for (i = 1; i < prodata.length; ++i)
if (specificBrand && prodata[i]['brand'] != specificBrand)
else if (specificPro && prodata[i]['name'] != specificPro)
else
if (prodata[i]['fun'] == 0 && perfIsChecked)
optionsmodel += '<li><a class="optionfuninit" href="#" data-proid="' + prodata[i]['id'] + '"><div class="listviewtexts"><span class="listviewtextsmodel">' + prodata[i]['model'] + '</span> - <span class="listviewtextspro">as surfed by ' + prodata[i]['name'] + '</span></div></a></li>';
else if (prodata[i]['fun'] == 1 && smallwaveIsChecked)
optionsmodel += '<li><a class="optionfuninit" href="#" data-proid="' + prodata[i]['id'] + '"><div class="listviewtexts"><span class="listviewtextsmodel">' + prodata[i]['model'] + '</span> - <span class="listviewtextspro">as surfed by ' + prodata[i]['name'] + '</span></div></a></li>';
else if (prodata[i]['fun'] == 2 && stepupIsChecked)
optionsmodel += '<li><a class="optionstepupinit" href="#" data-proid="' + prodata[i]['id'] + '"><div class="listviewtexts"><span class="listviewtextsmodel">' + prodata[i]['model'] + '</span> - <span class="listviewtextspro">as surfed by ' + prodata[i]['name'] + '</span></div></a></li>';
else if (prodata[i]['fun'] == 3 && smallwaveIsChecked)
optionsmodel += '<li><a class="optionkidsinit" href="#" data-proid="' + prodata[i]['id'] + '"><div class="listviewtexts"><span class="listviewtextsmodel">' + prodata[i]['model'] + '</span> - <span class="listviewtextspro">as surfed by ' + prodata[i]['name'] + '</span></div></a></li>';
if (prodata[i]['model'] !== prodata[i - 1]['model']) //eliminate name duplicates if prodata sorted by model
countmodels = countmodels + 1;
chosenmodelListViewHandle.html(optionsmodel);
if (chosenmodelListViewHandle.listview("option", "disabled"))
chosenmodelListViewHandle.listview("option", "disabled", false);
//~ if (resetModelsOnly)
//~ if ( !isNaN(previouslySelectedModelId) )
//~ chosenmodelListViewHandle.find('li[href="' + previouslySelectedModelId + '"]').attr("selected", "selected").siblings('li').removeAttr('selected');
//~
//~
//~ highlightFunModels(funfeatureOn, 1);
//~ highlightStepupModels(funfeatureOn, 0);
chosenmodelListViewHandle.listview("refresh", true);
$("#chosenmodel-button").addClass("ui-icon-carat-d ui-btn-icon-right");
if (!funfeatureOn)
else
$('ul#chosenmodel-menu').find("a.ui-btn:contains(SMALL-WAVE)").addClass("optionfun");
$('ul#chosenmodel-menu').find("a.ui-btn:contains(STEP-UP)").addClass("optionstepup");
prodata.sort(SortById); //we need this otherwise prodata is not usable by the $('#chosenpro').trigger
$("#chosenmodel-button span").attr( 'data-i18n': 'select.3' );
$("#boardselection").i18n();
listviewdeferred.resolve();
return listviewdeferred;
这会动态显示一长串图像和文本,具体取决于是否选中了过滤器(复选框),而且这个列表生成起来很长,而且在 ios 中滚动特别困难.... 性能很差。
你能帮我找出提高性能的方法吗?
【问题讨论】:
你可以只加载前 10 个项目,然后要么有一个 MORE 按钮不断添加到列表中,要么实现一些分页,这样一次只加载 10 个项目,你有 NEXT 和 PREVIOUS 按钮. 10 是任意的,您的页面大小可能会更大或更小,具体取决于您的项目中最适合的内容。 嗨@ezanker,感谢您的评论。我希望不给用户增加更多按钮的负担,所以我想找到一个不涉及添加按钮的解决方案。有没有办法检测列表上的滑动并加载更多项目? 见奥马尔的文章:jqmtricks.wordpress.com/2014/07/15/infinite-scrolling 【参考方案1】:接受的答案不正确。 您的代码很慢,但不是因为 jQuery Mobile。 看看 jsFiddle 中的循环:jsfiddle.net/L3gr46s8/4
for (i = 0; i <= 50; i++)
$('ul[data-role="listview"]').append('<li><a href="#">' + 'list item ' + i + '</a></li>');
$('ul[data-role="listview"]').listview('refresh');
这四行代码中有几个非常重要的问题。
首先,您的代码执行两次 DOM 遍历来定位页面上的 ul。您可以在循环之前执行$('ul[data-role="listview"]')
并将结果存储在变量中:
var listView = $('ul[data-role="listview"]');
其次,您的代码将列表项直接插入 DOM,并指示 jQuery Mobile 立即使用 .listview('refresh');
应用标记增强功能。 这非常昂贵!尤其是在功率不足的移动设备上。根据浏览器和页面布局,这两行可能会在循环的每次迭代中触发整个页面的重新绘制。
您应该将内容呈现为 DocumentFragment(在内存中),在一个操作中将其全部插入 DOM,并在最后告诉 JQM 增强标记一次。即使只是简单地将$('ul[data-role="listview"]').listview('refresh');
移出循环也会是一个巨大的改进。
这里有一些关于在将内容插入 DOM 之前先在内存中呈现内容的重要性的额外阅读:
How expensive is it to dynamically insert DIVs using JavaScript?
John Resig - DOM DocumentFragments
【讨论】:
【参考方案2】:简而言之,jQuery Mobile 很慢。
我的应用程序中有一个动态列表视图,并且在使用 jQuery Mobile 时也遇到了性能问题。我得出的结论是,问题出在渲染中,是由 jQuery Mobile 引起的。我实现了自己的样式,渲染时间从 170 毫秒降低到了 25 毫秒。
这是我的一些备份(3 篇文章):http://apachecordova.blogspot.fi/search/label/jQuery%20Mobile
编辑: 作为对您在 cmets 中的问题的回答,我认为如果我在这里发布我的代码不会有帮助。关键是你只写你需要的代码。我的列表视图可能与您的完全不同。
为了(再次)证明我的观点,我制作了两个列表视图。第一个是基本的 jQM 列表视图。另一个使用自定义 CSS 进行样式设置,它与我在应用程序中使用的非常接近。两者都有一个按钮可以呈现列表视图。幕后发生的事情非常不同: jQM: 如您所见,有很多东西(您可能不需要)正在进行
自定义 CSS: 为所有元素附加一个事件监听器,以使比较更公平
这些配置文件是用 Chrome 开发者工具记录的,区别很明显:173 毫秒 vs 12 毫秒。这个自定义 CSS 花了我大约 5 分钟的时间来写:
#custom-listview
list-style-type: none;
padding: 0px;
margin: 0px;
#custom-listview li
display: block;
position: relative;
overflow: visible;
#custom-listview a
display: block;
position: relative;
text-overflow: ellipsis;
text-decoration: none;
color: white;
padding: .7em 1em;
font-size: 16px;
background-color: #333;
border: solid 1px #1f1f1f;
overflow: hidden;
white-space: nowrap;
font-family: sans-serif;
我必须在此处添加一些代码,因为如果没有以下代码,我将无法链接到 Fiddle:
-
jQM
Custom CSS
我并不是说 jQuery Mobile 很糟糕。这对很多事情都有好处。但是如果你有复杂的结构和/或大量的数据,性能可能会成为一个问题,尤其是在 PhoneGap 应用程序中。这就是我的小经验得出的结论。
【讨论】:
啊哈 ;) 我喜欢你的第一句话!这就是为什么我将很快迁移到 Ionic。你的回答很有趣,我会读你的文章,我真的很愿意尝试这个。您认为您可以将自定义列表视图的代码发送给我,而不是使用 JQM 吗?谢谢。 嗨@balzafin,我采纳了你的建议并编写了我自己的列表以避免JQM 标记。在我的应用程序中确实感觉更快。但我认为还有另一个问题:我的列表显示图像并且滚动效果不是很好。也许是因为所有图像的总和太重了。 可能是。如果可以,请在没有图像、Lorem ipsum 或其他文本的情况下对其进行测试。至少你会发现这是不是这个原因。如果是这样,我能想到的唯一选项是分页或无限滚动,正如 ezanker 建议的那样。我并没有真正使用图片那么多,但也许你也可以尝试一些image optimization。 突然想到,如果滚动感觉很粘,您可以尝试 Velocity 滚动:-webkit-overflow-scrolling: touch;
。我用谷歌搜索了它,还找到了其他一些tips。在您的情况下最重要的是:始终预加载您的图像并隐藏大屏幕外的图像。因此,将它们全部加载,但以某种方式隐藏当前不在用户视图中的那些。可能值得一试。
@balzafin 你的代码很慢,但不是因为 jQuery Mobile。 看看你的 jsFiddle 中的循环:jsfiddle.net/L3gr46s8/4 你执行 两个 b> DOM 遍历以定位页面上的ul
。接下来,将列表项直接插入 DOM(根据浏览器触发整页重排)。最后,您告诉 jQuery Mobile 在插入 每个 项后应用标记增强.listview('refresh');
。您应该将内容呈现为 DocumentFragment(在内存中),在一个操作中将其全部插入 DOM,然后告诉 JQM 在最后增强标记 once。以上是关于Jquery Mobile + Phonegap 提升 listview 性能的主要内容,如果未能解决你的问题,请参考以下文章
jQuery Mobile 打破了 Phonegap deviceready 事件
iPhone上的jQuery Mobile + PhoneGap无法加载页面
Jquery (Mobile) 和 phonegap 存储模块不同步
一起使用 JQuery-Mobile/Phonegap 的正确方法?