2011-11-16 4 views
4

큰 view 데이터 집합을 내 viewModel에 가지고 있고 객체 배열을 반복하여 각 객체를 테이블 내에서 행으로 렌더링하는 경우 foreach을 사용하면 KnockoutJS는 렌더링 할 수있을 때까지 주 스레드를 차단하고, 때로는 몇 분이 걸립니다 (!).KnockoutJS foreach 블록 메인 스레드

다음은 urlcode을 포함하는 2000 개 개체를 포함하는 데이터 집합을 사용하는 jsFiddle 예제입니다. 실제 데이터는 경우에 따라 더 긴 URL과 4 개의 다른 열 (이 예에서는 2 개)을 갖게됩니다. 또한 스타일을 추가하면 프로세스 중에 약간 느려지는 것처럼 간단한 스타일이 추가되었습니다.

경고 : 브라우저 휴식

http://jsfiddle.net/DESC3/7/

+0

다음은이 문제를 보여줍니다. http://jsfiddle.net/DESC3/10/ 요점을 확인하는 데 필요하지 않으므로 데이터 양이 줄어 듭니다. – Esailija

+1

다음은 동일한 html (2000 행)이 다른 접근 방식으로 즉시 렌더링되는 방법입니다 (http://jsfiddle.net/DESC3/11/). 나는 이것을 말하는 것이 아니라 단지 녹아웃이 매우 최적화되어 있지 않다는 것을 보여줍니다 ... – Esailija

+0

@Esailija - 첫 번째 예는 실제로 실제로 빠릅니다. 이것은 1000 행 이상일 때만 우리에게 문제가됩니다. 모든 데이터를 하나의 데이터로 가져 오는 데는 몇 가지 이유가 있습니다. 따라서 페이지 매기기 또는 청크로 데이터를 보내는 것은 실제로 선택 사항이 아닙니다. – RyanScottLewis

답변

1

난 당신이 같은 대규모 데이터 세트가 있다면 당신은 대체 솔루션을 시도하는 것이 좋습니다. 예를 들어 slickGrid은 실제로 볼 수있는 데이터의 HTML 요소 만 생성하여 훨씬 더 효율적인 방식으로 큰 데이터 세트를 렌더링합니다. 우리는 이것을 대형 데이터 세트에 사용했으며 성능이 뛰어납니다.

1

어때? 예를 들어, 렌더링하고자하는 viewModel.items = ko.observableArray()이 있다고 가정 해보십시오.

  1. 관찰 할 수없는 데이터 배열 (예 : var itemsToRender = functionThatReturnsLargeArray())이 있습니다.
  2. itemsToRender의 데이터 일부를 관찰 가능한 배열에 넣으십시오. 50 개 요소 만 말해봐.
  3. setTimeout 콜백 내부의 부분에 관찰 가능한 배열에 요소를 계속 추가합니다.

주 1 : 당신은 약간의 시간 추적 setTimeout 콜백 및 증가에/당신이 각 반복에 추가 할 항목의 수를 줄일 추가 할 수 있습니다. 귀하의 목표는 각 콜백 시간을 50-100 밀리 초 이하로 유지하여 응용 프로그램이 여전히 반응이 느껴지도록하는 것입니다.

var batchSize = 50; // default number of items rendered per iteration 
var batchOffset = 0; 
function render(items, itemsToRender, done) { 
    setTimeout(function() { 
     var startTime = new Date().getTime(); 
     items.pushAll(itemsToRender.slice(batchOffset, batchSize)); 
     batchOffset += batchSize; 
     // at this point Knockout rendered next batchSize items from itemsToRender 
     var endTime = new Date().getTime(); 
     // update batchSize for next iteration 
     batchSize = batchSize * 50/(endTime - startTime); // 50 milliseconds 
     batchSize = Math.min(itemsToRender.length, batchOffset + batchSize); 
     if (batchSize > 0) render() else done(); // callback if you need one 
    }, 0); 
} 
/* I haven't actually tested the code */ 

다른 배치 크기 업데이트 전략은 대상 FPS를 기반으로 할 수 있습니다. 60fps의 업데이트 속도를 얻고 싶다고 가정하면 1000 밀리 초당 setTimeout 번의 60 회의 통화가 가능합니다. 전체 컬렉션을 처리하는 데 다소 시간이 걸릴 것입니다. setTimeout 대신 requestAnimationFrame을 사용하고 어떻게 작동하는지 확인할 수도 있습니다.

편집 : Build-in throttlingKnockout JS 1.3에 추가되었다 (현재는 베타 버전입니다하지만 꽤 안정적인 것 같습니다).


NOTE2 : 뷰에 다른 데이터가 viewModel.items에 의존하는 경우 당신은 여전히 ​​원래의 배열 itemsToRender에 아래로 매핑 할 수 있습니다. 예를 들어 수집 할 항목 수를 표시하려고한다고 가정 해보십시오. viewModel.items().length을 사용하면 더 많은 항목이 렌더링되는 동안 UI에서 크기 값이 변경되므로 결국됩니다. 이를 방지하려면 먼저 itemsToRender을 기준으로 dependentObservable으로 크기 바인딩을 정의해야합니다 (viewModel.items이 아님).모든 항목을 렌더링 한 후에는 적합하다고 생각되면 viewModel.items에 다시 매핑 할 수 있습니다.