Performance 如何避免每次在KnockoutJS中重新绘制整个选项卡?
在这个使用引导标签的非常基本的例子中,每次我点击一个标签,KnockoutJS都会重新绘制整个标签,有没有办法“保存”标签的状态,这样下次我点击消息时,KO就不必重新绘制整个内容了 绘制大约需要100毫秒。(根据Chromium profiler),fps降至1fps。这让整个事情变得毫无反应Performance 如何避免每次在KnockoutJS中重新绘制整个选项卡?,performance,knockout.js,twitter-bootstrap-3,redraw,Performance,Knockout.js,Twitter Bootstrap 3,Redraw,在这个使用引导标签的非常基本的例子中,每次我点击一个标签,KnockoutJS都会重新绘制整个标签,有没有办法“保存”标签的状态,这样下次我点击消息时,KO就不必重新绘制整个内容了 绘制大约需要100毫秒。(根据Chromium profiler),fps降至1fps。这让整个事情变得毫无反应 <div> <ul class="nav nav-tabs" role="tablist"> <li role="presentation"><a h
<div>
<ul class="nav nav-tabs" role="tablist">
<li role="presentation"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">Profile</a></li>
<li role="presentation"><a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">Messages</a></li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="tab_storage" data-bind="with: BuildingsVM">
<h2 data-bind="i18next: 'AllOurStorage'"></h2>
<div class="row" data-bind="foreach: buildingsByType['RawStorage']">
<div data-bind="template: { name: 'tpl_building' }"></div>
</div>
<div class="row" data-bind="foreach: buildingsByType['RefinedStorage']">
<div data-bind="template: { name: 'tpl_building' }"></div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="messages" data-bind="messages">
<div data-bind="foreach: message">
<span data-bind="text: title"></span>
</div>
</div>
</div>
</div>
编辑:
使用引导JS库切换选项卡
$("#MenuTab").on('click','a', function(e) {
e.preventDefault();
var id = $(this).attr('href');
$(this).tab("show");
});
建筑“卡”的模板:
<script type="text/html" id="tpl_building">
<div class="col-xs-10 col-sm-6 col-md-3">
<div class="card card-raised">
<div class="card-header"><h3 data-bind="text: buildingName"></h3> </div>
<div class="card-content">
<div class="row" data-bind="if: (buildingType == 'RawStorage' || buildingType == 'RefinedStorage')">
<div class="col-xs-4">
Usage: <span data-bind="text: getOccupyingSpace"></span> / <span data-bind="text: TotalCapacity"></span>
</div>
<div class="col-xs-8">
<span data-bind="meter: {value: getOccupyingSpace, max: TotalCapacity}"></span>
</div>
</div>
<div class="row">
<!-- ko foreach: Ressources -->
<div class="col-md-6" data-bind="if: $parent.TotalCapacity">
<span data-bind="text: Name"></span> <span data-bind="text: Qte"></span> (<span class="small" data-bind="text: getOccupyingSpace"></span>)
<!-- ko if: ProductionRate() -->
<span data-bind="text: ProductionRate()"></span>/s
<!-- /ko -->
</div>
<div class="col-md-6" data-bind="if: ProductionRate">
<span data-bind="text: Name"></span> <span data-bind="text: getProductionRatePerSecond()"></span>/s (1 every <span data-bind="text: ProductionRate()/1000"></span> sec)
</div>
<!-- /ko -->
</div>
</div>
<div class="card-footer">
<!-- ko if: initialConstructionGoing -->
<i class="fa fa-exclamation-triangle icon-gradient-warning" class="ko-popover"
data-bind="popover: initialConstructionGoing, popoverOptions: { title: i18nextko.t('UnderConstruction_potitle') }" ></i>
<span data-bind="i18next: 'UnderConstruction'"></span> <span data-bind="text: getBuildProgress"></span>
<span data-bind="meter: {p: getBuildProgress, onComplete: 'remove'}"></span>
<span data-bind="WorkersAssigned().length"></span>/<span data-bind="WorkersBuild"></span>
<!-- /ko -->
</div>
</div>
</div>
</script>
还修改了原始选项卡内容代码以反映真实代码(使用模板)
我本来会制作一个JSFIDLE,但由于一些模糊的原因,它似乎不能正常工作(新的路由器固件可能是问题所在,阻塞CDN)
为了更好地理解上下文,您可以看看我的另一个问题:
查看控制台中的DOM树,即使选项卡未处于活动状态,所有元素仍然存在-可能是其他原因造成了延迟。是否使用自定义绑定?如何切换哪个选项卡处于活动状态?哦,哈哈,是的,这可能会有所帮助,更新了问题。哇,在调查了探查器中的所有调用之后,似乎边框图像花了太多时间才光栅化。删除该类属性后,chromium的渲染速度似乎更快,并且没有200毫秒的显示时间(平均只有40毫秒)-不知道CSS会对性能产生如此大的影响!
<script type="text/html" id="tpl_building">
<div class="col-xs-10 col-sm-6 col-md-3">
<div class="card card-raised">
<div class="card-header"><h3 data-bind="text: buildingName"></h3> </div>
<div class="card-content">
<div class="row" data-bind="if: (buildingType == 'RawStorage' || buildingType == 'RefinedStorage')">
<div class="col-xs-4">
Usage: <span data-bind="text: getOccupyingSpace"></span> / <span data-bind="text: TotalCapacity"></span>
</div>
<div class="col-xs-8">
<span data-bind="meter: {value: getOccupyingSpace, max: TotalCapacity}"></span>
</div>
</div>
<div class="row">
<!-- ko foreach: Ressources -->
<div class="col-md-6" data-bind="if: $parent.TotalCapacity">
<span data-bind="text: Name"></span> <span data-bind="text: Qte"></span> (<span class="small" data-bind="text: getOccupyingSpace"></span>)
<!-- ko if: ProductionRate() -->
<span data-bind="text: ProductionRate()"></span>/s
<!-- /ko -->
</div>
<div class="col-md-6" data-bind="if: ProductionRate">
<span data-bind="text: Name"></span> <span data-bind="text: getProductionRatePerSecond()"></span>/s (1 every <span data-bind="text: ProductionRate()/1000"></span> sec)
</div>
<!-- /ko -->
</div>
</div>
<div class="card-footer">
<!-- ko if: initialConstructionGoing -->
<i class="fa fa-exclamation-triangle icon-gradient-warning" class="ko-popover"
data-bind="popover: initialConstructionGoing, popoverOptions: { title: i18nextko.t('UnderConstruction_potitle') }" ></i>
<span data-bind="i18next: 'UnderConstruction'"></span> <span data-bind="text: getBuildProgress"></span>
<span data-bind="meter: {p: getBuildProgress, onComplete: 'remove'}"></span>
<span data-bind="WorkersAssigned().length"></span>/<span data-bind="WorkersBuild"></span>
<!-- /ko -->
</div>
</div>
</div>
</script>
ko.bindingHandlers.meter = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
var progress = ko.unwrap(valueAccessor());
if(progress.value) {
var value = ko.unwrap(progress.value);
var max = ko.unwrap(progress.max);
if(value === 0 && max === 0) {
var percentage = 0;
} else {
var percentage = (value/max)*100;
}
} else {
var percentage = ko.unwrap(progress.p);
}
$progress_tpl = $('<div class="meter"><span style="width: '+percentage+'%"></span></div>');
if(progress.onComplete) {
$progress_tpl.attr('data-oncomplete', progress.onComplete);
}
$(element).append($progress_tpl);
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// This will be called once when the binding is first applied to an element,
// and again whenever any observables/computeds that are accessed change
// Update the DOM element based on the supplied values here.
var progress = ko.unwrap(valueAccessor());
if(progress.value) {
var value = ko.unwrap(progress.value);
var max = ko.unwrap(progress.max);
var percentage = (value/max)*100;
} else {
var percentage = ko.unwrap(progress.p);
}
$(element).find('.meter span').stop().animate({'width': percentage+'%'}, (bindingContext.$root.cf.refreshEvery), 'swing', function() {
if(percentage >= 100) {
if($(element).find(".meter").attr('data-oncomplete') == 'remove') {
$(element).hide(300, function() {
$(element).remove();
ko.cleanNode(element);
});
} else if($(element).find(".meter").attr('data-oncomplete') == 'reset') {
$(element).hide(300, function() {
$(element).remove();
});
} else {
}
}
});
}
};
var mainVM = {
Ressources: new RessourcesVM(),
Buildings: new BuildingsVM(),
cf: {
...
}
}