Vue.js 원본 읽기, 2

6950 단어

초기화


먼저 코드의 입구부터 시작하여 전체 버전의 경우 platforms/web/entry-runtime-with-compiler.js 파일에서
/* platforms/web/entry-runtime-with-compiler.js */
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)

  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to  or  - mount to normal elements instead.`
    )
    return this
  }

  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) {
    let template = options.template
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)
          /* istanbul ignore if */
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            )
          }
        }
      } else if (template.nodeType) {
        template = template.innerHTML
      } else {
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) {
      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile')
      }

      const { render, staticRenderFns } = compileToFunctions(template, {
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns

      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile end')
        measure(`vue ${this._name} compile`, 'compile', 'compile end')
      }
    }
  }
  return mount.call(this, el, hydrating)
}

먼저 Vue$mount 방법을 확대했다. render 방법을 제공하지 않고 template 옵션을 제공하거나 el 옵션을 통해 HTML의 어떤 요소 아래의 내용을 모델로 지정하면 innerHTML 또는 getOuterHTML으로 문자열 형식의 모델을 얻는다.그리고 compileToFunctions을 사용하여 render 함수로 컴파일합니다.마지막으로 원래의 $mount 방법을 호출한다.전체 버전이 runtime 버전보다 사실상 더 많은 절차라는 것이다.편역 과정은 mount 이전에 완성되었다.
이 Vue는 platforms/web/runtime/index.js에서 도입되었습니다.
/* platforms/web/runtime/index.js*/
// install platform specific utils
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement

// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop

// public mount method
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

// devtools global hook
/* istanbul ignore next */

이 파일은 플랫폼과 관련된 도구 함수를 연결하고 $mount 함수를 연결하며 마운트할 때 최종적으로 mountComponent 방법을 사용합니다.
Vue 도입은 core/index.js
/* core/index.js  */
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
...
initGlobalAPI(Vue)
...
export default Vue
core/index.js에는 initGlobalAPI, Vue에는 Vue.set, Vue.delete, Vue.nextTick, Vue.use, Vue.extend 등 전역 API에 Vue을 추가했다.
/* core/instance/index.js */
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

드디어 core/instance/index.js이 정의한 곳을 보았습니다. 여기서 Vue의 구조 함수를 정의한 다음에 Mixin의 방식으로 Vue이라는 구조 함수의 확장 기능을 정의했습니다.Vue 방법은 this._init을 호출할 때 initMixin(Vue)에 추가한 것으로 Vue.prototype에 정의되었다.
/* core/instance/init.js */
export function initMixin (Vue: Class) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

    let startTag, endTag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}
core/instance/init.js은 다음과 같은 몇 가지 일을 했다. 통합 설정, 생명주기 초기화, 이벤트, 렌더링, 호출 initMixin 갈고리, injection 초기화, state 초기화,provide 초기화, beforeCreate 갈고리를 호출한 다음에 created을 호출하여 마운트한다.

좋은 웹페이지 즐겨찾기