美文网首页
Vue源码05-mount

Vue源码05-mount

作者: 熊少年 | 来源:发表于2020-05-25 11:56 被阅读0次

    我们在各种初始化都完成的情况下开始Vue挂载

      if (vm.$options.el) {
          vm.$mount(vm.$options.el)  // 调用的是web/entry-runtime-with-compiler.js
        }
    

    mount函数

    // runtime/index.js 运行时的$mount
    Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean
    ): Component {
      el = el && inBrowser ? query(el) : undefined
      return mountComponent(this, el, hydrating)
    }
    // entry-runtime-with-compiler.js
    const mount = Vue.prototype.$mount
    Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean
    ): Component {
      el = el && query(el)
      const options = this.$options
      // 转化template/el 为render函数
      if (!options.render) {
        let template = options.template
        if (template) {  // 解析template
          if (typeof template === 'string') {
            if (template.charAt(0) === '#') {  // template='#id'
              template = idToTemplate(template) // idToTemplate去查看源代码
            }
          } 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) // 通过el获取template
        }
        if (template) {
          // 对template-->render
          const { render, staticRenderFns } = compileToFunctions(template, {
            outputSourceRange: process.env.NODE_ENV !== 'production',
            shouldDecodeNewlines,
            shouldDecodeNewlinesForHref,
            delimiters: options.delimiters,
            comments: options.comments
          }, this)  // compileToFunctions去源码查看
          options.render = render // // 生成的render函数with(this){return _c("div") }
          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) // 运行是的mount
    }
    

    模版编译createCompiler

    export const createCompiler = createCompilerCreator(function baseCompile (
      template: string,
      options: CompilerOptions
    ): CompiledResult {
      // 解析ast
      const ast = parse(template.trim(), options)
    
      if (options.optimize !== false) {
        optimize(ast, options)
      }
    
      const code = generate(ast, options)
    
      return {
        ast,
        render: code.render, // with(this){return _c("div") }
        staticRenderFns: code.staticRenderFns
      }
    })
    
    • parse函数是解析出ast,里面就是正则匹配解析template
    • generate根据ast生成render
      这里介绍一下ast
    declare type ASTElement = {
      type: 1;
      tag: string;
      attrsList: Array<ASTAttr>;
      attrsMap: { [key: string]: any };
      rawAttrsMap: { [key: string]: ASTAttr };
      parent: ASTElement | void;
      children: Array<ASTNode>;
    
      start?: number;
      end?: number;
    
      processed?: true;
    
      static?: boolean;
      staticRoot?: boolean;
      staticInFor?: boolean;
      staticProcessed?: boolean;
      hasBindings?: boolean;
    
      text?: string;
      attrs?: Array<ASTAttr>;
      dynamicAttrs?: Array<ASTAttr>;
      props?: Array<ASTAttr>;
      plain?: boolean;
      pre?: true;
      ns?: string;
    
      component?: string;
      inlineTemplate?: true;
      transitionMode?: string | null;
      slotName?: ?string;
      slotTarget?: ?string;
      slotTargetDynamic?: boolean;
      slotScope?: ?string;
      scopedSlots?: { [name: string]: ASTElement };
    
      ref?: string;
      refInFor?: boolean;
    
      if?: string;
      ifProcessed?: boolean;
      elseif?: string;
      else?: true;
      ifConditions?: ASTIfConditions;
    
      for?: string;
      forProcessed?: boolean;
      key?: string;
      alias?: string;
      iterator1?: string;
      iterator2?: string;
    
      staticClass?: string;
      classBinding?: string;
      staticStyle?: string;
      styleBinding?: string;
      events?: ASTElementHandlers;
      nativeEvents?: ASTElementHandlers;
    
      transition?: string | true;
      transitionOnAppear?: boolean;
    
      model?: {
        value: string;
        callback: string;
        expression: string;
      };
    
      directives?: Array<ASTDirective>;
    
      forbidden?: true;
      once?: true;
      onceProcessed?: boolean;
      wrapData?: (code: string) => string;
      wrapListeners?: (code: string) => string;
    };
    

    mountComponent

    export function mountComponent (
      vm: Component,
      el: ?Element,
      hydrating?: boolean
    ): Component {
      vm.$el = el
      if (!vm.$options.render) {
             vm.$options.render = createEmptyVNode
             ...
      }
     // 必须保证render函数存在
      callHook(vm, 'beforeMount') // 这里调用beforeMount钩子
    
      let updateComponent
      ...
        updateComponent = () => {
          vm._update(vm._render(), hydrating)  // 先调用_render,在调用_update
        }
    
      new Watcher(vm, updateComponent, noop, {
        before () {
          if (vm._isMounted && !vm._isDestroyed) {
            callHook(vm, 'beforeUpdate')
          }
        }
      }, true /* isRenderWatcher */)  // RenderWatcher
      hydrating = false
    
    
      if (vm.$vnode == null) {
        vm._isMounted = true
        callHook(vm, 'mounted') // 调用mounted
      }
      return vm
    }
    

    关于挂载组件最好是自己去单步调试,查看一下细节,这里做一下简单介绍

    • 先调用_render函数拿到vnode
    • 在调用_update函数
    • 在调用 patch函数
    • 先 createElm(vnode, insertedVnodeQueue)
    • 最后在 removeVnodes([oldVnode], 0, 0)

    相关文章

      网友评论

          本文标题:Vue源码05-mount

          本文链接:https://www.haomeiwen.com/subject/krzeahtx.html