>  기사  >  웹 프론트엔드  >  Sizzle의 “컴파일 원리”에 대한 간략한 논의_기타

Sizzle의 “컴파일 원리”에 대한 간략한 논의_기타

WBOY
WBOY원래의
2016-05-16 16:04:20803검색

Sizzle은 jQuery 작성자인 John Resig가 작성한 DOM 선택기 엔진으로, 속도는 업계 최초로 알려져 있습니다. 독립적인 새로운 선택기 엔진으로 jQuery 버전 1.3 이후에 등장했으며 John Resig에 의해 오픈 소스 프로젝트로 채택되었습니다. Sizzle은 독립적인 부분이며 어떤 라이브러리에도 의존하지 않습니다. jQuery를 사용하지 않으려면 Sizzle을 사용하거나 Mool, Dojo, YUI 등 다른 프레임워크에서 사용할 수 있습니다.

얼마 전 jQuery에 대한 공유 PPT를 준비하던 중 동료들에게 jQuery 사용 방법 외에 더 알고 싶은 것이 있는지 물었습니다. 누군가 jQuery 선택기가 어떻게 구현되는지 알고 싶다고 했습니다. , 일부 사람들은 jQuery의 쿼리 속도가 다른 프레임워크와 어떻게 비교되는지 언급했습니다. 속도에 관해서는 sizzle 공식 웹사이트에서 테스트 예제를 다운로드할 수 있습니다. 속도는 실제로 매우 유리합니다. 하지만 이렇게 효율적인 실행 속도를 갖는 이유는 여기서 논의하고 싶은 구현 원리와 관련이 있습니다.

Sizzle을 이해하기 전에 먼저 선택기가 무엇인지 이해해야 합니다. 다음은 jQuery에 익숙한 학생이라면 이 선택기 형식에도 익숙해야 합니다.

코드 복사 코드는 다음과 같습니다.

태그 #id .class , a:first

기본적으로 왼쪽에서 오른쪽으로 깊이 필터링하여 일치하는 DOM 요소를 찾습니다. 이 문은 그리 복잡하지 않습니다. 이 쿼리문을 직접 구현하는 것은 어렵지 않습니다. 그러나 쿼리 문에는 기본 규칙만 있고 선택자의 수와 순서가 고정되어 있지 않습니다. 우리가 직접 코드를 작성할 때 이러한 임의의 배열과 조합에 어떻게 적응할 수 있습니까? Sizzle은 다양한 상황에 대한 정상적인 분석과 실행을 달성할 수 있습니다.

Sizzle의 소스 코드는 참으로 복잡하고 아이디어를 이해하기 어렵습니다. 패키징의 외부 레이어는 제쳐두고 개인적으로 전체 구현의 핵심이라고 생각하는 세 가지 방법을 직접 살펴보겠습니다.

첫 번째 핵심 방법. 소스 코드의 1052번째 줄에 토큰화 기능이 있습니다:

코드 복사 코드는 다음과 같습니다.

함수 토큰화(선택기, 구문 분석 전용) { }

두 번째 매개변수인parseOnly는 false입니다. 이는 토큰 직렬화 작업만 수행되고 결과가 반환되지 않음을 의미합니다. 이 경우 직렬화 결과는 나중에 사용할 수 있도록 캐시됩니다. 선택기는 쿼리 문입니다.

이 함수로 처리된 후, 예를 들어 selector="#idtag.class, a:first"가 전달되면 다음 형식과 유사한 결과를 얻을 수 있습니다.

[
[
{matches:" id ",type:"ID"},
{matches:" tag ",type:"TAG"},
{matches:" class ",type:"CLASS"},
...
],
[
    {matches:" a",type:"TAG"},
    ...
],
[…],
…
]


토큰화 함수의 이름과 그 기능을 보면 '컴파일 원리'라는 단어가 쉽게 떠오릅니다. 여기서는 어휘 분석과 약간 비슷하지만 이 어휘 분석은 프로그램을 컴파일할 때 수행하는 어휘 분석보다 간단합니다.

토큰화 방법은 선택기의 쉼표, 공백 및 관계 선택기의 정규 표현을 기반으로 "단어 분할"을 수행하고 2차원 배열을 얻습니다(이 부정확한 이름을 사용해도 됩니다). -차원 배열 쉼표로 구분되며 소스 코드에서는 그룹이라고 합니다.

소스 코드를 다시 살펴보겠습니다. 405번째 줄부터 Expr = Sizzle.selectors = {}의 정의가 있습니다. 567번째 줄에 필터의 정의가 있습니다. 여기에서 기본 필터링 유형을 찾을 수 있습니다: "ID ", "TAG", "CLASS", "ATTR", "CHILD", "PEUDO" 등이 토큰화되어 최종적으로 분류되는 유형입니다.

"단어 분할"이 완료된 후에도 405행에 정의된 Expr= Sizzle.selectors = {}를 살펴보세요. 우리에게 익숙한 모든 선택기는 여기에서 찾을 수 있으며 각 선택기는 메서드 정의에 해당합니다. 이 시점에서 Sizzle이 실제로 선택기에서 "단어 분할"을 수행하고 이를 분할한 다음 Expr에서 해당 메소드를 찾아 특정 쿼리나 필터링 작업을 수행하는지 생각해 봐야 합니다.

대답은 기본적으로 '그렇다'입니다. 하지만 Sizzle은 좀 더 구체적이고 영리한 접근 방식을 가지고 있습니다. 제가 매우 핵심이라고 생각하는 두 번째 방법을 살펴보겠습니다.

소스 코드의 1293번째 줄에 matcherFromTokens 함수가 있습니다:

코드 복사 코드는 다음과 같습니다.

함수 matcherFromTokens(tokens) { }

전달된 매개변수는 토큰화 메소드에서 가져옵니다. Matcher는 말 그대로 "매칭 프로그램"으로 이해될 수 있습니다. 이 기능의 기능은 토큰을 통해 매칭 프로그램을 생성하는 것입니다. 사실 그렇습니다. 지면의 제약으로 인해 이 글에서는 제가 이해하고 있는 Sizzle의 구현 원리 중 일부만 공유할 것이며, 소스 코드는 게시하지 않을 것입니다. 나중에 시간이 나면 더 자세한 소스 코드 분석 기사를 편집할 수도 있습니다.

MatcherFromTokens 메소드는 이전 가정을 확인합니다. 이는 선택기 "단어 분할"과 Expr에서 정의된 매칭 방법 사이의 연결 및 링크 역할을 합니다. 선택기의 다양한 순열 및 조합이 적용 가능하다고 할 수 있습니다. Sizzle의 영리한 점은 얻은 "단어 분할" 결과를 Expr의 메소드와 하나씩 직접 매칭하여 실행하는 것이 아니라, 먼저 규칙에 따라 대규모 매칭 메소드를 결합하여 실행한다는 것입니다. 마지막 단계에서. 그러나 조합 후 실행 방법은 세 번째 핵심 방법에 따라 다릅니다.

소스 코드의 1350번째 줄에 superMatcher 메소드가 있습니다:

코드 복사 코드는 다음과 같습니다.

superMatcher = 함수( 시드, 컨텍스트, xml, 결과, ExpandContext ) { }

이 메소드는 직접 정의된 메소드는 아니며 1345행의 matcherFromGroupMatchers(elementMatchers, setMatchers) 메소드를 통해 반환되지만 최종 실행에서 중요한 역할을 합니다.

superMatcher 메소드는 매개변수 seed, ExpandContext 및 context를 기반으로 시작 쿼리 범위를 결정합니다. 이는 시드에서 직접 쿼리하고 필터링할 수도 있고 컨텍스트 또는 컨텍스트의 상위 노드 범위 내에 있을 수도 있습니다. 시드에서 시작하지 않으면 먼저 Expr.find["TAG"]( "*", ExpandContext && context.parentNode || context ) 코드를 실행하고 elems 컬렉션(배열)을 기다립니다. 그런 다음 요소 순회를 수행하고 미리 생성된 matcher 메서드를 사용하여 요소를 하나씩 일치시킵니다. 결과가 true이면 요소가 반환된 결과 집합에 직접 쌓입니다.

여기에서 matcher 메소드의 원래 결과는 모두 bool 값이므로 405행으로 돌아가서 Expr의 필터에 포함된 메소드를 살펴보겠습니다. 그들은 모두 bool 값을 반환합니다. PSEUDO(pseudo-class)에 해당하는 더 많은 의사 클래스 메서드를 포함하는 것은 동일합니다. 알고 보니 레이어별로 확인하는 것이 아니라 앞뒤로 이동하여 매칭과 필터링을 하는 것과 비슷합니다. Expr에서는 find 및 preFilter 반환 세트만 있습니다.

결과 세트를 얻기 위해 왜 일일이 매칭하고 필터링하는 방식을 사용하는지에 대해서는 여전히 의문이 들지만, Sizzle의 가장 기본적인 '컴파일 원리'는 명확하게 설명되었어야 한다고 생각합니다.

하지만 의심을 버릴 수는 없습니다. 계속해 보겠습니다. 사실 이 글 자체는 거꾸로 쓴 것 같습니다. 소스 코드를 보는 데 관심이 있는 학생들은 처음에는 이 세 가지 주요 방법을 볼 수 없습니다. 실제로 Sizzle은 이 세 가지 메서드를 입력하기 전에 일련의 다른 작업도 수행합니다.

Sizzle의 실제 입구는 소스 코드의 220번째 줄에 있다고 할 수 있습니다:

코드 복사 코드는 다음과 같습니다.

함수 Sizzle( 선택기, 컨텍스트, 결과, 시드 ){ }

이 메소드의 첫 번째 단락은 비교적 이해하기 쉽습니다. 선택기가 단일 ID 선택기(#id)와 일치할 수 있는 경우에는 context.getElementById(m) 메소드를 사용하여 요소를 직접 찾을 수 있습니다. ID. 선택기가 단일 TAG 선택기와 일치할 수 있는 경우 먼저 context.getElementsByTagName(selector) 메서드를 사용하여 관련 요소를 찾습니다. 현재 브라우저가 기본 getElementsByClassName만 사용하고 일치하는 선택기가 단일 CLASS 선택기인 경우 context.getElementsByClassName(m) 메서드도 관련 요소를 찾는 데 사용됩니다. 이 세 가지 메소드는 모두 브라우저에서 지원하는 네이티브 메소드로, 실행 효율이 단연 최고입니다.

가장 기본적인 방법을 사용할 수 없는 경우 선택 방법으로 들어갑니다. 소스 코드의 1480행에는 다음과 같은 정의가 있습니다.

코드 복사 코드는 다음과 같습니다.

함수 선택( 선택기, 컨텍스트, 결과, 시드, xml ) { }

select 메소드에서는 앞서 언급한 "단어 분할" 작업이 먼저 선택기에서 수행됩니다. 그러나 이 작업 후에 우리는 일치 방법의 조립을 직접 시작하지 않고 먼저 몇 가지 찾기 작업을 수행했습니다. 여기서 찾기 작업은 Expr의 찾기에 해당할 수 있습니다. 쿼리 작업을 수행하고 결과 집합을 반환합니다.

Select는 "단어 분할"을 통해 얻은 선택자를 사용하여 해당 유형에 따라 find 메소드를 사용하여 검색할 수 있는 결과 집합을 먼저 찾는 것으로 이해될 수 있습니다. 찾기 작업을 수행하면 결과 집합이 선택기 순서에 따라 왼쪽에서 오른쪽으로 좁아집니다. 순회 후 선택기의 모든 선택기가 찾기 작업을 수행할 수 있으면 결과가 직접 반환됩니다. 그렇지 않으면 앞서 설명한 "컴파일" 및 필터링 프로세스로 들어갑니다.

이 시점에서 여러분도 Sizzle의 워크플로우를 기본적으로 이해할 수 있습니다. 위에 남겨진 질문은 실제로 이 시점에서는 더 이상 질문이 아닙니다. 왜냐하면 역 매칭 필터링을 수행할 때 해당 검색 범위는 이미 레이어별로 필터링된 가장 작은 집합이기 때문입니다. 역방향 일치 필터링 방법은 실제로 의사 클래스와 같이 해당하는 선택기에 대한 효율적인 선택입니다.

Sizzle이 왜 효율적인지 간단히 요약해 보겠습니다.

우선 처리 흐름 측면에서 항상 가장 효율적인 기본 방식을 사용하여 먼저 처리합니다. 이전에 소개한 것은 Sizzle 자체의 셀렉터 구현 방식뿐이다. 실제로 Sizzle이 실행되면 현재 브라우저가 querySelectorAll 네이티브 메소드(소스 코드 라인 1545)를 지원하는지 여부를 먼저 판단하게 된다. 지원된다면 이 방법이 먼저 사용됩니다. 브라우저에서 기본적으로 지원하는 방법은 Sizzle 자체 js에서 작성된 방법보다 확실히 더 효율적입니다. 이를 먼저 사용하면 Sizzle의 작업 효율성도 높아질 수 있습니다. (querySelectorAll에 대한 자세한 내용은 온라인에서 확인할 수 있습니다.) querySelectorAll 메소드가 지원되지 않는 경우 Sizzle은 문제 해결을 위해 getElementById, getElementsByTag, getElementsByClassName 및 기타 메소드를 직접 사용할 수 있는지 여부를 결정하는 데 우선순위를 부여합니다.

둘째, 상대적으로 복잡한 상황에서 Sizzle은 항상 기본 메서드를 사용하여 최대한 많이 쿼리하고 선택하여 선택 범위를 좁힌 다음 앞서 소개한 "컴파일 원리"를 사용하여 범위를 좁히는 방식을 선택합니다. 선택 범위. 요소가 하나씩 일치하여 필터링됩니다. "컴파일" 단계까지의 작업 흐름은 약간 복잡하고 이전 방법에 비해 효율성이 확실히 약간 떨어지겠지만 Sizzle은 이러한 방법을 가능한 한 적게 사용하려고 노력하는 동시에 동시에 다음과 같은 방법도 시도하고 있습니다. 더 높은 효율성을 얻기 위해 이러한 방법으로 처리된 결과 세트를 가능한 한 작고 단순하게 만듭니다.

다시 , 이 "컴파일" 프로세스에 들어간 후에도 Sizzle은 프로세스를 먼저 설명하기 위해 일시적으로 무시하고 도입하지 않은 캐싱 메커니즘도 구현했습니다. 소스 코드의 1535번째 줄은 우리가 "컴파일" 항목이라고 부르는 부분입니다. 이는 세 번째 핵심 메소드인 superMatcher를 호출한다는 의미입니다. 라인 1466을 추적하여 확인하십시오. 컴파일 메소드는 선택기를 기반으로 생성된 일치 함수를 캐시합니다. 그뿐만 아니라, 1052행의 토큰화 메소드를 살펴보십시오. 이는 실제로 선택기를 기반으로 단어 분할 결과를 캐시합니다. 즉, Sizzle(선택기) 메서드를 한 번 실행하고 다음 번에는 Sizzle(선택기) 메서드를 직접 호출한 후에는 그 내부에서 가장 성능을 많이 소비하는 "컴파일" 프로세스가 너무 많은 성능을 소비하지 않고 이전 캐시가 메서드를 직접 가져옵니다. 소위 "컴파일"의 가장 큰 이점 중 하나는 캐싱을 용이하게 한다는 점일 수 있다고 생각합니다. 여기서 소위 "컴파일"은 전처리된 함수를 생성하고 나중에 사용하기 위해 저장하는 것으로도 이해될 수 있습니다.

이쯤 되면 이전에 선택자의 구현 원리와 실행 효율성에 대해 우려했던 학생들의 질문에 기본적으로 답변할 수 있었으면 좋겠습니다. 또한, 이 글의 분석 결론은 Sizzle 최신 버전의 소스 코드에서 도출되었습니다. 글에서 언급된 코드 행 번호는 이 버전의 소스 코드에 적용되며, http: //sizzlejs.com/. 시간이 부족하므로 분석에 불완전한 점이 있으면 양해해 주시기 바랍니다. 아직 궁금한 점이 있는 학생은 오프라인에서도 계속 소통할 수 있습니다.

위 내용은 이 글의 전체 내용입니다. 모두 마음에 드셨으면 좋겠습니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.