JQuery each() 함수는 순환 DOM 구조의 성능을 최적화하는 방법

6777 단어
만약 jQuery라는 물건을 사용하는 차원에만 머물러 구체적으로 실현될 수 없다면 정말 문제를 일으키기 쉽다.이것도 바로 최근에 내가 jQuery를 별로 추앙하지 않는 이유이다. 이 프레임워크의 API 설정은 사람들이 잘못된 길로 들어서도록 오도한 혐의가 있다.
 
  
$.fn.beautifyTable = function(options) {
// , options
return this.each(function() {
var table = $(this),
tbody = table.children('tbody'),
tr = tbody.children('tr'),
th = tbody.children('th'),
td = tbody.children('td');
// class
table.addClass(option.tableClass);
th.addClass(options.headerClass); //1
td.addClass(options.cellClass); //2
// class
tbody.children('tr:even').addClass(options.evenRowClass); //3
tbody.children('tr:odd').addClass(options.oddRowClass); //4
//
tr.children('th,td').css('text-align', options.align); //5
//
tr.bind('mouseover', addActiveClass); //6
tr.bind('mouseout', removeActiveClass); //7
//
tr.bind('click', toggleClickClass); //8
});
};

전반적으로 말하면 이 코드는 괜찮고 사고방식이 뚜렷하며 논리가 명확하며 무엇을 하고 싶은지도 주석을 통해 명확하게 말했다.그러나 저자의 견해에 따르면, 표에 120줄이 있을 때, IE는 스크립트의 운행 시간이 너무 길다는 것을 반영했다.분명히 표현으로 볼 때, 이 함수의 효율은 높지 않고, 심지어는 극히 낮다고 말할 수도 있다.
그래서 코드 차원에서 분석을 시작했다. 이것은 표준 jQuery 플러그인식 함수로 전형적인return this가 있다.each(function( ) { ... };);형식의 코드, 만약에 작가가 이 코드를 썼을 때 본 선과에 따라 생각하지 않았다면 jQuery의 함수 하나가 무슨 일을 했는지 알아야 한다.
간단하게 말하자면, jQuery.fn의 함수는 대부분이 each의 호출이다. 이른바 each는 자연히 선택한 원소를 두루 훑어보고 특정한 원소를 지정한 조작을 한다.그러면 위의 코드를 보고 얼마나 많은 과정을 거쳤는지 살펴보자. 여기서 120줄만 선택했다고 가정하고 한 줄에 6열이 있고 한 줄의 표두를 더하자.
th를 누비고 헤더클래스를 추가합니다. 원소 수는 6입니다.
td를 누비고cellClass를 추가합니다. 원소수는 6*120=720입니다.
모든tr에서 홀수를 찾아내려면 모든tr를 한 번 훑어보아야 한다. 원소수는 120이다.
홀수의tr를 옮겨다니며 이벤트RowClass를 추가합니다. 원소수는 120/2=60입니다.
모든tr에서 짝수를 찾아내려면 모든tr를 한 번 훑어보아야 한다. 원소수는 120이다.
짝수의tr를 옮겨다니며oddRowClass를 추가합니다. 원소수는 120/2=60입니다.
모든th와td를 두루 돌아다니며text-align을 추가합니다. 원소수는 120*6+6=726입니다.
모든tr를 누비고mouseover 이벤트를 추가합니다. 원소 수는 120입니다.
모든tr를 옮겨다니며,mouseout 이벤트를 추가합니다. 원소 수는 120입니다.
모든tr를 훑어보고 클릭 이벤트를 추가합니다. 원소 수는 120입니다.
편의를 위해서, 우리는 간단하게 한 요소를 훑어보는 데 10ms가 걸린다고 가정한다. 그러면 이 함수는 모두 얼마나 걸렸을까?이 함수는 모두 2172개의 원소를 만났는데 21720ms, 즉 21초가 걸렸다. 분명히 IE는 스크립트가 너무 오래 실행되었음을 보고해야 한다.
효율이 낮은 원인을 알았으니 근본적으로 해결해야 한다. 자연히 온갖 방법을 다 동원하여 순환을 합병해야 한다. 처음에 보면 위 코드에 주석된 숫자에 따라 적어도 아래의 몇 가지는 합병할 수 있다.
3과 4는 한 번의 순환으로 통합돼 120+60+120+60에서 120으로 240이 줄어든다.1, 2와 5는 한 번의 순환으로 통합돼 6+720+726에서 726으로 726 감소했다.6, 7, 8은 한 번의 순환으로 통합해 120+120+120에서 120으로 240 감소했다.더욱이 3, 4는 6, 7, 8과 마찬가지로 한 번의 순환으로 통합돼 120을 계속 줄였다.누적하여 말하자면, 우리는 모두 240+726+240+120=1326차례의 원소 조작을 감소시켰고, 총계는 13260ms이다.최적화된 후에 우리의 함수 소모 시간은 21720-13260=8460ms, 즉 8s로 바뀌었다.
여기에 의문이 하나 있을 수 있다. 표의 구조상 모든th와 td원소는tr안에 있을 것이다. 그러면 왜 1, 2, 5의 세 단계의 순환을 똑같이tr에 대한 순환에 두지 않고 끼워 넣는 순환을 형성하지 않는가? 그러면 더욱 빠르지 않겠는가?
여기에 이렇게 하지 않은 데는 두 가지 이유가 있다.
첫째, 1, 2, 5 이 세 가지를 어디에 두든지 모든th와 td 요소에 대한 접근을 줄일 수 없다.
한편, $('th, td') 이 선택기는 sizzle에서 getElements ByTagName 함수로 두 번 번역되어 호출되며, 첫 번째는th를 가져오고, 두 번째는td를 가져오고, 집합의 병합을 진행합니다.getElementsByTagName은 내장 함수이기 때문에 이 함수는 순환이 없는 것으로 볼 수 있다. 즉, 복잡도는 O(1)이고 같은 집합의 귀합은 Array와 관련된 함수를 사용하며 메모리에 대한 작업이고 복잡도는 O(1)이다.
반대로tr원소에 대한 순환에서 $('th,'td'라는 선택기를 사용하면 tr원소에서 getElementsByTagName을 두 번 호출합니다. 어느 원소에서든 이 함수를 호출하면 함수가 실행되는 시간이 같기 때문에 tr원소를 순환할 때 사용하면 오히려 119*2회의 함수를 더 호출하여 효율이 떨어지지 않습니다.
이를 통해 알 수 있듯이sizzle 선택기에 대한 기본 지식은 jQuery 코드를 최적화하는 데 도움을 주는 중요한 부분이다.
아무것도 자바스크립트에 시키지 마세요.
앞의 기본적인 최적화에 따라 21초에서 8초로 시간을 줄였지만 8초라는 숫자는 받아들일 수 없었다.
다시 한 번 우리의 코드를 분석하면 사실상 순환은 언어 차원의 내용으로 그 속도가 상당히 빨라야 한다.모든 요소에 대한 동작은 jQuery가 제공하는 함수로 반복적으로 보면 대부분의 자원을 차지하는 주자이다.반복적으로 요소에 접근할 때 10ms를 사용한다면, 최소한 100ms급의 소모를 실행하는 것은 천만에요.
따라서 효율을 한층 더 최적화하기 위해서는 원소에 대한 조작을 줄이는 데 착수해야 한다.코드를 다시 자세히 살펴보면 이 함수는 적어도 다음과 같은 양식에 대한 수정이 매우 많은 것을 발견할 수 있다.
모든th에class를 더해라.
모든 td에class를 추가합니다.
tr점 짝짓기 줄에class를 추가합니다.
모든 th와 td에 text-align 스타일을 추가합니다.
사실상 우리는 CSS 자체가 하위 선택기를 가지고 있다는 것을 알고 있다. 브라우저는 원래 CSS에 대한 해석을 하는데 자바스크립트가 요소에 일일이class를 붙이는 것보다 효율이 훨씬 높다.
따라서 CSS를 제어할 수 있다면 이 함수는headerClass,cellClass 두 가지 설정 항목을 가지지 말고 가능한 한 CSS에서 설정해야 한다.
 
  
.beautiful-table th { /* headerClass */ }
.beautiful-table td { /* cellClass */ }

게다가tr의 짝짓기 스타일은 일부 브라우저에서:nth-child 위조를 사용할 수 있으며 이 부분은 특성 탐색을 이용하여 이 위조를 지원하지 않는 브라우저에서만addClass를 사용하여 스타일을 추가할 수 있습니다.물론 IE 시리즈를 최적화하고 싶을 뿐이라면 무시할 수 있습니다.
nth-child 위조 클래스에 대한 탐측은 다음과 같은 사고방식으로 진행할 수 있다. 스타일시트를 만들고 규칙을 만들 수 있다. 예를 들어 #test span: nth-child(odd) {display:block;}.상응하는 HTML 구조를 만들고 id는test의div이며 내부에 3개span을 배치합니다.
stylesheet과 div를 함께 포함하는 DOM 트리입니다.첫 번째와 세 번째 span의 실행 기간 디스플레이 스타일을 보십시오. Block이면 이 위조 클래스를 지원합니다.생성된 스타일시트와 div를 삭제하고 캐시 탐지 결과를 잊지 마십시오.마지막으로 모든th와 td 요소에 text-align 스타일을 추가하는 것도 css를 통해 최적화할 수 있습니다.어느 Align을 추가했는지 모르면 몇 가지 스타일을 더 쓰십시오.
 
  
/* CSS */
.beautiful-table-center th,.beautiful-table-center td { text-align: center !important; }
.beautiful-table-rightright th,.beautiful-table-rightright td { text-align: rightright !important; }
.beautiful-table-left th,.beautiful-table-left td { text-align: left !important; }
/* javascript */
table.addClass('beautiful-table-' + options.align);

물론 위에서 말한 최적화는 CSS에 대한 통제권이 있는 상황에서 이루어진 것이다. 만약에 그 자체가 CSS 양식에 접촉할 수 없다면 예를 들어 이것은 통용되는 플러그인 함수로 완전히 통제할 수 없는 제3자에게 사용될 것이다. 그러면 어떻게 해야 합니까?전혀 방법이 없는 것도 아니다.
페이지에 있는 모든 CSS 규칙, 예를 들어 문서를 찾습니다.styleSheets.모든 규칙을 훑어보고 설정 항목의 헤더클래스,cell클래스 등을 꺼내세요.필요한 몇 개의class의 모든 스타일을 추출하여 뷰티풀 테이블 th와 같은 새로운 선택기로 조립합니다.생성된 선택기를 사용하여 새 스타일시트를 생성하고 DOM 트리에 추가합니다.그럼 테이블에 뷰티풀 테이블만 붙이면 돼요.
물론 위의 방법도 사실 시간을 많이 소모한다. 왜냐하면 스타일시트를 두루 훑어보고 스타일시트를 만들어야 하기 때문이다.구체적으로 효율 향상에 큰 도움이 되는지 여부는 페이지의 규모에 따라 서로 다른 효과가 있을 수 있다. 사용 여부는 함수 디자이너의 구체적인 수요에 달려 있다. 여기서 전략을 제시한다.
전반적으로 자바스크립트를 최대한 적게 실행하고 더 많은 양식화된 작업을 CSS에 맡기면 브라우저의 렌더링 엔진이 완성하고 이 함수를 더욱 최적화할 수 있다. 만약에 addClass, css에 대한 호출이 100ms가 필요하다고 가정하면 이번 최적화는 원래의 120+726=846번의 조작을 직접 없앴다.84600ms의 시간을 절약했다(당연히 과장된 성분이 있지만 전체 함수의 소모에 있어서 이것은 확실히 매우 큰 부분이다).
이 글은 jQuery의 각 실현 차원에서 최적화를 하고자 했을 뿐이다. jQuery 전체 운행 과정에 대한 분석, 세부 소개와 최적화 방향만 언급했을 뿐 기본적인 최적화 방법을 언급하지 않았다. 예를 들어 전체 테이블을 DOM 트리에서 제거하고 모든 조작을 완성한 후에 DOM으로 돌려보내서 Repaint를 줄인다.mouseover와mouseout을mouseenter와mouseleave로 변경하여 정확한 이벤트 거품 모델로 인한 중복된 이벤트 함수의 실행을 감소합니다.th, td와 같은 단순한 요소의 선택은 원생 get Elements ByTagName을 우선적으로 사용하고sizzle 분석 선택기를 없애는 시간을 고려합니다.
마지막으로 이 글은 전방 개발자에 대해 브라우저가 블랙박스일 수 있지만 많은 프레임워크, 도구, 라이브러리가 개방되어 있기 때문에 사용하기 전에 어느 정도 이해할 수 있다면 개인의 기술 향상과 최종 제품의 품질 최적화에 도움이 될 것이다.'그것을 알고 있지만 그 이유를 모른다'는 것은 매우 꺼리는 상황이다.

좋은 웹페이지 즐겨찾기