Vue 의 copile 조작 방법

9943 단어 vuecompile
Vue 에서 템 플 릿 컴 파일 도 매우 중요 한 부분 이 고 그 안에 도 매우 복잡 합 니 다.이번 탐 구 는 모든 디 테 일 을 깊이 탐구 하지 않 고 전경 개 요 를 걸 어 갑 니 다.자,여러분 과 저 는 함께 끝까지 알 아 보 겠 습 니 다.
첫 체험
우 리 는 Vue 의 초기 화 함 수 를 보면 알 수 있 습 니 다.마지막 단계 에서vm.$mount(el) 작업 을 했 습 니 다.이$mount 는 두 곳 에서 정 의 했 습 니 다.각각entry-runtime-with-compiler.js(약칭:eMount)과 runtime/index.js(약칭:rMount)두 파일 에서 이 두 가 지 는 어떤 차이 가 있 습 니까?

// entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount //    $mount      rMount
Vue.prototype.$mount = function (
 el?: string | Element,
 hydrating?: boolean
): Component {
 const options = this.$options
 if (!options.render) {
 ...
 if(template) {
  const { render, staticRenderFns } = compileToFunctions(template, {
  shouldDecodeNewlines,
  shouldDecodeNewlinesForHref,
  delimiters: options.delimiters,
  comments: options.comments
  }, this)
  options.render = render
  options.staticRenderFns = staticRenderFns
 }
 ...
 }
 return mount.call(this, el, hydrating)
}
사실 eMount 는 마지막 에 rMount 를 호출 합 니 다.eMount 에서 일정한 조작 을 했 을 뿐 입 니 다.render 함 수 를 제공 하면 rMount 를 직접 호출 합 니 다.없 으 면 template 를 제공 하 는 지 찾 아 갑 니 다.template 를 제공 하지 않 으 면 dom 으로 template 를 만 드 는 지 알 아 봅 니 다.마지막 으로 컴 파일 을 통 해 render 함 수 를 되 돌려 주 고 eMount 를 호출 합 니 다.
위 에서 알 수 있 듯 이 가장 중요 한 부분 은 copile ToFunctions 라 는 함수 입 니 다.마지막 으로 render 함 수 를 되 돌려 주 었 습 니 다.이 함수 에 대해 좀 복잡 합 니 다.제 가 그림 을 그 려 서 관 계 를 보 았 는데 오차 가 있 을 수 있 습 니 다.협객 들 이 지적 해 주 셨 으 면 좋 겠 습 니 다.

컴 파일 3 단계 걷 기
이 컴 파일 의 전체 과정 을 살 펴 보면 가장 핵심 적 인 부분 은 바로 여기에 들 어 오 는 baseCompile 이 하 는 일 이라는 것 을 알 수 있다.
  • parse:첫 번 째 단 계 는 template 를 추상 문법 트 리(AST)로 전환 해 야 합 니 다.
  • optimizer:두 번 째 단 계 는 이 추상 문법 트 리 에 대해 정적 노드 표 시 를 하면 렌 더 링 과정 을 최적화 할 수 있 습 니 다.
  • generateCode:세 번 째 단 계 는 AST 에 따라 render 함수 문자열 을 생 성 합 니 다.
  • 자,이제 하나씩 천천히 봅 시다.
    해석 기
    해석 기 에 매우 중요 한 개념 인 AST 가 있 으 니 여러분 이 직접 알 아 보 세 요.
    Vue 에서 ASTnode 는 몇 가지 유형 으로 나 뉘 는데 ASTnode 에 대한 정 의 는 flow/copile.js 에 있 습 니 다.다음 그림 을 보십시오.

    우 리 는 간단 한 예 로 설명 한다.
    
    <div id="demo">
     <h1>Latest Vue.js Commits</h1>
     <p>{{1 + 1}}</p>
    </div>
    이 코드 가 어떤 AST 를 만 들 지 생각해 볼 까요?

    우리 의 이 예 가 마지막 에 생 성 된 것 은 대략 이런 나무 입 니 다.그러면 Vue 는 어떻게 이런 해석 을 합 니까?계속 보 자.
    parse 함수 에서 우 리 는 먼저 매우 많은 전역 속성 과 함 수 를 정의 한 다음 에 parseHTML 이라는 함 수 를 호출 했 습 니 다.이것 도 parse 의 가장 핵심 적 인 함수 입 니 다.이 함 수 는 템 플 릿 을 계속 해석 하고 루트 를 채 우 며 마지막 으로 루트(AST)를 되 돌려 줍 니 다.
    parseHTML
    이 함수 에서 가장 중요 한 것 은 while 순환 의 코드 이 고 분석 과정 에서 중요 한 역할 을 하 는 것 은 몇 가지 정규 표현 식 이 있 습 니 다.
    
    const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
    const ncname = '[a-zA-Z_][\\w\\-\\.]*'
    const qnameCapture = `((?:${ncname}\\:)?${ncname})`
    const startTagOpen = new RegExp(`^<${qnameCapture}`)
    const startTagClose = /^\s*(\/?)>/
    const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
    const doctype = /^<!DOCTYPE [^>]+>/i
    const comment = /^<!\--/
    const conditionalComment = /^<!\[/
    Vue 는 위 에 있 는 몇 개의 정규 표현 식 을 통 해 시작 탭,탭 이름,속성 등 을 일치 시 킵 니 다.
    while 에 대한 자세 한 설명 은 제 창고 에 두 었 습 니 다.관심 이 있 으 면 가 보 세 요.
    while 에서 사실은 끊임없이html.indexOf('<')로 일치 한 다음 에 되 돌아 오 는 색인 에 따라 서로 다른 해석 처 리 를 하 는 것 입 니 다.
  • __0:이것 은 주석,조건 주석,doctype,시작 탭,끝 탭 중 하나 입 니 다
  • .
  • __크 면 0:이것 은 텍스트,표현 식
  • 임 을 설명 합 니 다.
  • __0: 이하html 태그 가 해석 되 었 음 을 표시 합 니 다.텍스트,표현 식
  • 이 남 을 수 있 습 니 다.
    parse 함 수 는 이 작업 을 계속 반복 한 다음 에 template 를 AST 로 바 꾸 는 것 입 니 다.분석 과정 에서 태그 와 태그 사이 의 빈 칸 에 대해 Vue 도 최적화 처 리 를 했 습 니 다.일부 요소 간 의 빈 칸 은 쓸모 가 없습니다.
    copile 은 사실 매우 많은 지면 을 말 해 야 하지만 여 기 는 간단하게 생각 을 정리 할 수 밖 에 없고 구체 적 인 코드 는 여러분 이 내 려 가서 깊이 잠 겨 야 합 니 다.
    최적화 기
    코드 에 있 는 설명 을 통 해 알 수 있 듯 이 최적화 기의 목적 은 AST 에서 정적 인 서브 트 리 를 찾 는 것 입 니 다.
    순 정적 서브 트 리 를 상수 로 올 리 고 다시 렌 더 링 할 때마다 새로운 노드 를 만 들 필요 가 없습니다.
    패 치 에서 넘 어 갈 수 있어 요.
    optimize 의 코드 양은 parse 만큼 많 지 않 습 니 다.어디 보 자.
    
    export function optimize (root: ?ASTElement, options: CompilerOptions) {
     //    root     
     if (!root) return
     //           
     // 'type,tag,attrsList,attrsMap,plain,parent,children,attrs'
     isStaticKey = genStaticKeysCached(options.staticKeys || '')
     //             ,html    svg  
     isPlatformReservedTag = options.isReservedTag || no
     //      :                    
     markStatic(root)
     //      :         
     markStaticRoots(root, false)
    }
    아래 두 단락 의 코드 를 나 는 모두 일부분 을 잘 랐 다.왜냐하면 좀 많 기 때문에 여기에 코드 를 많이 붙 이지 않 을 것 이다.상세 한 상황 은 나의 창 고 를 참고 하 시기 바 랍 니 다.
    편력
    
    function markStatic (node: ASTNode) {
     node.static = isStatic(node)
     if (node.type === 1) {
     ...
     }
    }
    사실 markStatic 는 재 귀적 인 과정 으로 AST 의 노드 를 계속 검사 한 다음 에 표 시 를 한다.
    방금 우리 가 말 했 듯 이 AST 노드 는 세 가지 로 나 뉘 는데 isStatic 이라는 함수 에서 우 리 는 서로 다른 유형의 노드 에 대해 판단 했다.
    
    function isStatic (node: ASTNode): boolean {
     if (node.type === 2) { // expression
     return false
     }
     if (node.type === 3) { // text
     return true
     }
     return !!(node.pre || (
     !node.hasBindings && // no dynamic bindings
     !node.if && !node.for && // not v-if or v-for or v-else
     !isBuiltInTag(node.tag) && // not a built-in
     isPlatformReservedTag(node.tag) && // not a component
     !isDirectChildOfTemplateFor(node) &&
     Object.keys(node).every(isStaticKey)
     ))
    }
    Vue 가 다음 몇 가지 상황 을 처리 한 것 을 볼 수 있 습 니 다.
    이 노드 의 type 이 2,즉 표현 식 노드 일 때 정적 노드 가 아 닌 것 이 분명 하기 때문에 false 로 돌아 갑 니 다.
    type 이 3 일 때 텍스트 노드 입 니 다.정적 노드 입 니 다.true 로 돌아 갑 니 다.
    요소 노드 에 v-pre 를 사용 하거나<pre>탭 을 사용 하면 이 노드 에 pre 를 true 로 추가 합 니 다.이것 은 정적 노드 입 니 다.
    정적 노드 라면 동적 바 인 딩 이 필요 하지 않 습 니 다.v-if,v-for,v-else 등 명령 이 있 으 면 안 됩 니 다.slot 나 component 태그 가 아 닙 니 다.우리 가 정의 한 태그 가 아 닙 니 다.부모 노드 나 요소 가 없 는 부모 노드 는 v-for 가 있 는 template 이 아 닙 니 다.이 노드 의 속성 은 모두 type,tag,attrsList,attrsMap,plain,parent,children 에 있 습 니 다.attrs 에서 이러한 조건 을 만족 시 키 면 정적 인 노드 라 고 생각 합 니 다.
    그 다음 에 AST 에 대해 재 귀 작업 을 하고 정태 적 인 노드 를 표시 합 니 다.안에 어떤 조작 을 했 는 지 위 에 있 는 창고 에 가서 볼 수 있 습 니 다.여 기 는 전개 되 지 않 습 니 다.
    두 번 째 편력
    두 번 째 로 옮 겨 다 니 는 과정 은 정적 뿌리 노드 를 표시 하 는 것 이다.그러면 우 리 는 정적 뿌리 노드 에 대한 정의 가 무엇 인지 먼저 뿌리 노드 의 뜻 은 그 가 잎 노드 가 될 수 없고 적어도 하위 노드 가 있어 야 하 며 정적 이다.여기 서 Vue 는 설명 을 했 습 니 다.만약 에 정적 노드 가 하나의 키 노드 만 가지 고 이 서브 노드 가 텍스트 노드 라면 정적 처 리 를 하지 않 고 수익 보다 비용 이 많 으 므 로 직접 과장 하 는 것 이 좋 습 니 다.
    마찬가지 로 우 리 는 함수 에서 끊임없이 재 귀적 으로 표 시 를 하고 마지막 으로 모든 정적 루트 노드 에 staticRoot 의 표 시 를 추가 하면 이 코드 에 대해 서도 위의 창고 에 가서 볼 수 있다.
    코드 생 성기
    이 함수 에서 우 리 는 AST 를 render 함수 문자열 로 변환 합 니 다.코드 의 양 이 매우 많 습 니 다.우 리 는 한번 볼 수 있 습 니 다.
    
    export function generate (
     ast: ASTElement | void,
     options: CompilerOptions
    ): CodegenResult {
     //           
     const state = new CodegenState(options)
     //    render    
     const code = ast ? genElement(ast, state) : '_c("div")'
     return {
     render: `with(this){return $[code]}`,
     staticRenderFns: state.staticRenderFns
     }
    }
    마지막 코드 생 성 단계 에서 가장 중요 한 함 수 는 genElement 이라는 함수 입 니 다.서로 다른 명령,속성 에 대해 우 리 는 서로 다른 코드 생 성 함 수 를 선택 할 것 입 니 다.마지막 으로 우 리 는 AST 에 따라 문자열 을 만 듭 니 다.다음 과 같 습 니 다.
    
    with(this){return _c('div',{attrs:{"id":"demo"}},[(1>0)?_c('h1',[_v("Latest Vue.js Commits")]):_e(),...}
    render 라 는 함수 문자열 에서 우 리 는 일부 함 수 를 볼 수 있 습 니 다.그러면 이 함수 들 은 어디에서 정 의 됩 니까?우 리 는 core/instance/index.js 이 파일 에서 이 함 수 를 찾 을 수 있 습 니 다.
    
    // v-once
    target._o = markOnce
    //   
    target._n = toNumber
    target._s = toString
    // v-for
    target._l = renderList
    // slot
    target._t = renderSlot
    //     
    target._q = looseEqual
    //             
    target._i = looseIndexOf
    //      
    target._m = renderStatic
    //      
    target._f = resolveFilter
    //      
    target._k = checkKeyCodes
    // v-bind
    target._b = bindObjectProps
    //       
    target._v = createTextVNode
    //      
    target._e = createEmptyVNode
    //    scopeslot
    target._u = resolveScopedSlots
    //       
    target._g = bindObjectListeners
    //    VNode   
    vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
    컴 파일 이 끝 난 후에 우 리 는 서로 다른 명령,속성 등에 따라 어떤 처리 함 수 를 호출 해 야 하 는 지 선택 하고 마지막 으로 함수 문자열 로 연결 합 니 다.
    마지막 으로 render 렌 더 링 문자열 이 생 성 되 었 습 니 다.어떻게 사용 해 야 합 니까?사실 뒤에서 렌 더 링 을 할 때 우 리 는new Function(render) 작업 을 한 후에 우 리 는 render 함 수 를 정상적으로 사용 할 수 있 습 니 다.
    총결산
    큰 절 차 를 거 친 후에 저 는 여러분 들 이 컴 파일 과정 에 대해 비교적 명확 한 인식 을 가지 게 될 것 이 라 고 믿 습 니 다.그리고 디 테 일 한 부분 을 파 서 믿 는 것 도 훨씬 쉬 워 질 것 입 니 다.소스 코드 를 읽 는 것 은 읽 기 위해 읽 는 과정 이 아니 라 소스 코드 에서 우리 가 매일 개발 할 때 알 지 못 할 지식 을 많이 배 울 수 있 습 니 다.
    마지막 코드 생 성기 의 큰 코드 에 대해 저 는 아직 주석 을 다 달 지 못 했 습 니 다.그 다음 에 소스 코드 주석 을 창고 에 넣 을 것 입 니 다.하지만 여러분 도 소스 코드 를 잘 읽 을 수 있 을 것 이 라 고 믿 습 니 다.
    또 하 나 는 render 함수 에서 Vue 는 with 함 수 를 사 용 했 습 니 다.우 리 는 평소에 본 적 이 없 을 것 입 니 다.공식 적 으로 with 를 사용 하 는 것 을 추천 하지 않 기 때문에 저 는 이런 생각 으로 원인 을 찾 았 습 니 다.마지막 으로 저 는 지식 에서 특히 큰 대답 을 찾 았 습 니 다.이것 은 링크 입 니 다.여러분 이 알 아 보 셔 도 됩 니 다.
    위 에서 말 한 것 은 편집장 님 께 서 소개 해 주신 Vue 의 copile 입 니 다.여러분 께 도움 이 되 셨 으 면 좋 겠 습 니 다.궁금 한 점 이 있 으 시 면 메 시 지 를 남 겨 주세요.편집장 님 께 서 바로 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

    좋은 웹페이지 즐겨찾기