vue2源码学习
浮川的小窝

vue2源码学习

面壁人浮川
2023-12-31 发布 / 正在检测是否收录...

8765d1644e60479da66551f9f851b667.jpeg


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    html,body{
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
    }
  </style>
</head>
<body>
  <div id="app"></div>
  <script src="../dist/vue.js"></script>
  <script>
    Vue.component("my",{
      render(h){
        return h('div','我是组件测试组件渲染流程')
      }
    })
    let vm = new Vue({
      el:"#app",
      render(h){
        return h('my')
      }
    })
  </script>
</body>
</html>
流程
// (一)创建全局组件
  /* src/core/global-api/assets.js  */
  1.definition = this.options._base.extend(definition) // 执行
  2.this.options[type + 's'][id] = definition // 在vue实例 options上绑定组件名称 

  /* src/core/global-api/extend.js */
  1.Vue.extend (){
       // ...
       return Sub
  } // 执行extend方法并返回Sub
  2.const Sub = function VueComponent (options) {
      this._init(options)
    } 
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    ) // 创建sub子类并合并vue函数上定义的各种方法

// (二)创建vue对象 (一)创建全局组件
   /* src/core/instance/index.js  */
   1.function Vue (options) {
      this._init(options)
    } // 创建vue对象并执行_init方法
   /* src/core/instance/init.js */
   2.Vue.prototype._init = function (options?: Object) {
        // ...
        initLifecycle(vm) // 绑定父子关系
        initEvents(vm) // 初始化时间
        initRender(vm) // 初始化渲染
        callHook(vm, 'beforeCreate') // 生命周期
        initInjections(vm) // inject
        initState(vm) // data属性监听 响应式
        initProvide(vm) // provide
        callHook(vm, 'created')  // 生命周期
        // ...
        if (vm.$options.el) {
          vm.$mount(vm.$options.el)
        }
   } // 初始化方法 响应式等 vm.$mount执行挂载
   /* src/platforms/web/entry-runtime-with-compiler.js */
   3.Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean
    ): Component {
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns
      return mount.call(this, el, hydrating)
    } // 重写Vue.prototype.$mount方法 解析template模板 绑定到this上 执行原$mount方法

    /* src/platforms/web/runtime/index.js */
    4.Vue.prototype.$mount = function (
        el?: string | Element,
        hydrating?: boolean
      ): Component {
        el = el && inBrowser ? query(el) : undefined
        return mountComponent(this, el, hydrating)
      } // 执行mountComponent
    /* src/core/instance/lifecycle.js */
    5.export function mountComponent (
        vm: Component,
        el: ?Element,
        hydrating?: boolean
      ): Component {
        callHook(vm, 'beforeMount')
        
        updateComponent = () => {
          vm._update(vm._render(), hydrating)
        }
        new Watcher(vm, updateComponent, noop, {
          before () {
            if (vm._isMounted && !vm._isDestroyed) {
              callHook(vm, 'beforeUpdate')
            }
          }
        }, true /* isRenderWatcher */)
      } // 创建组件渲染Watcher
     /* src/core/observer/watcher.js  */
     6.export default class Watcher {
        constructor (){
          if (typeof expOrFn === 'function') {
            this.getter = expOrFn
          }
          this.value = this.lazy
            ? undefined
            : this.get()
        }
        get () {
          pushTarget(this)
          let value
          const vm = this.vm
          try {
            value = this.getter.call(vm, vm)
          } catch (e) {
            if (this.user) {
              handleError(e, vm, `getter for watcher "${this.expression}"`)
            } else {
              throw e
            }
          } finally {
            if (this.deep) {
              traverse(value)
            }
            popTarget()
            this.cleanupDeps()
          }
          return value
        }
     } // 渲染watcher获取  获取回调 updateComponent = () => {vm._update(vm._render(), hydrating)} 执行
     /* src/core/instance/render.js  */
     7.Vue.prototype._render = function (): VNode {
        vnode = render.call(vm._renderProxy, vm.$createElement)
     } // 执行render(h){ return h('my') }h为$createElement方法创建vnode
     /* src/core/vdom/create-element.js */
     8.export function createElement (
        context: Component,
        tag: any,
        data: any,
        children: any,
        normalizationType: any,
        alwaysNormalize: boolean
      ): VNode | Array<VNode> {
        return _createElement(context, tag, data, children, normalizationType)
      } // 创建vnode元素
      export function _createElement (
        context: Component, // vue实例
        tag?: string | Class<Component> | Function | Object,
        data?: VNodeData,
        children?: any,
        normalizationType?: number
      ): VNode | Array<VNode> {
          let Ctor
          if (typeof tag === 'string') {
             else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) // 获取全局组件绑定在vue函数上的全局组件
             {
                // component
                vnode = createComponent(Ctor, data, context, children, tag) // 创建vnode
              } 
          }
      }// 创建Ctor和vnode元素
      /* src/core/vdom/create-component.js */
      9.export function createComponent (
          Ctor: Class<Component> | Function | Object | void, // 子类my 合并了vue上的¥options
          data: ?VNodeData,
          context: Component, // vue实例对象
          children: ?Array<VNode>,
          tag?: string
        ): VNode | Array<VNode> | void {
          const baseCtor = context.$options._base //_base等于vue函数  Vue (options) {this._init(options)}
          // extract props
          const propsData = extractPropsFromVNodeData(data, Ctor, tag)
          const listeners = data.on
          // replace with listeners with .native modifier
          // so it gets processed during parent component patch.
          data.on = data.nativeOn
          // install component management hooks onto the placeholder node
          installComponentHooks(data)
          // 创建一个包裹住my组件的父容器组件 vue-component-1-my
          const vnode = new VNode(
            `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
            data, undefined, undefined, undefined, context,
            { Ctor, propsData, listeners, tag, children },
            asyncFactory
          )
           else if (isDef(vnode)) {
            if (isDef(ns)) applyNS(vnode, ns)
            if (isDef(data)) registerDeepBindings(data)
            return vnode
          }
        } // 创建包裹my子组件的父组件vue-component-1-my的vnode元素
        // 查看图1 返回第7步 
        /* src/core/instance/render.js  */
        10.Vue.prototype._render = function (): VNode {
          return vnode
        } // 返回vue-component-1-my vnode 
        // 返回第5步 updateComponent
        /* src/core/instance/lifecycle.js */
        11.Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
           const vm: Component = this // 这个vm应为vue创建的的实例对象
            if (!prevVnode) {
              // initial render
              vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
            } // 更新页面
        }
        /* src/core/vdom/patch.js */
        12. return function patch (oldVnode, vnode, hydrating, removeOnly) {
          const isRealElement = isDef(oldVnode.nodeType)
          if (isRealElement) {
            oldVnode = emptyNodeAt(oldVnode) // oldVnode = #app元素赋值
          }
          // create new node
          createElm(
            vnode,
            insertedVnodeQueue,
            // extremely rare edge case: do not insert if old element is in a
            // leaving transition. Only happens when combining transition +
            // keep-alive + HOCs. (#4590)
            oldElm._leaveCb ? null : parentElm,
            nodeOps.nextSibling(oldElm)
          )
        }
        function createElm (
          vnode, // vue-component-1-my
          insertedVnodeQueue, // []
          parentElm, // body dom元素
          refElm,
          nested,
          ownerArray,
          index
        ) {
          if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
            return
          }
        }
        function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
           if (isDef(i = i.hook) && isDef(i = i.init)) {
              i(vnode, false /* hydrating */)
            }// 执行组件 hooks生命周期 (init,prepatch,insert,destroy)中的init
        }
        /* src/core/vdom/create-component.js  */
        13.const componentVNodeHooks = {
            init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
             else {
                const child = vnode.componentInstance = createComponentInstanceForVnode(
                  vnode,
                  activeInstance
                )
                child.$mount(hydrating ? vnode.elm : undefined, hydrating)
              }
            },
        }
        export function createComponentInstanceForVnode (
          vnode: any, 
          parent: any,
        ): Component {
          const options: InternalComponentOptions = {
            _isComponent: true,
            _parentVnode: vnode,
            parent
          }
          // check inline-template render functions
          const inlineTemplate = vnode.data.inlineTemplate
          return new vnode.componentOptions.Ctor(options)
        } // 执行全局组件my的this._init(options)方法
        // 返回(一)创建全局组件 第2步 查看图二options参数的值
        // 再次执行(二)创建vue对象 第2步 执行完后回到第13步
        child.$mount(hydrating ? vnode.elm : undefined, hydrating)
        // 再次执行4、5、6步后 返回第12步
        // 其中第五步vm._update(vm._render(), hydrating) 
        // vm._render()返回my组件的vnode
        // vm._update执行最终页面dom渲染
        /* src/core/instance/lifecycle.js  */
        Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
          const vm: Component = this
          if (!prevVnode) {
            // initial render
            vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
          } else {
            // updates
            vm.$el = vm.__patch__(prevVnode, vnode)
          }
          restoreActiveInstance()
          if (prevEl) {
            prevEl.__vue__ = null
          }
          if (vm.$el) {
            vm.$el.__vue__ = vm
          }
          // if parent is an HOC, update its $el as well
          if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
            vm.$parent.$el = vm.$el
          }
        } // 执行__patch__ 更新页面 将最终真实#app 下render函数中的
        // 内容转换为真实dom赋值给vm.$el
        /* src/core/vdom/patch.js  */
        14.return function patch (oldVnode, vnode, hydrating, removeOnly) {
           createElm(vnode, insertedVnodeQueue)
          return vnode.elm
        }
        function createElm (
          vnode,
          insertedVnodeQueue,
          parentElm,
          refElm,
          nested,
          ownerArray,
          index
        ) {
          // ...
          vnode.elm = vnode.ns
            ? nodeOps.createElementNS(vnode.ns, tag)
            : nodeOps.createElement(tag, vnode) // 创建my组件 vnode中的elm属性 = dom元素
          createChildren(vnode, children, insertedVnodeQueue) // 递归创建my组件中的子元素标签(已转换为vnode)
          insert(parentElm, vnode.elm, refElm) // 递归插入到子元素的父级elm中
        }// 将my组件嵌套的vode中的elm创建出来
        // 返回到patch return vnode.elm
        // 返回到第13步_update
        // 返回到6步 value = this.getter.call(vm, vm)
        // 返回到第5步 mountComponent
        // 返回到第4步 runtime/index.js Vue.prototype.$mount
        // 返回到第3步 entry-runtime-with-compiler.js Vue.prototype.$mount
        // 返回到第13步 componentVNodeHooks
        // 返回到第9步 vdom/create-component.js createComponent
        function initComponent (vnode, insertedVnodeQueue) {
          if (isDef(vnode.data.pendingInsert)) {
            insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert)
            vnode.data.pendingInsert = null
          }
          vnode.elm = vnode.componentInstance.$el
          if (isPatchable(vnode)) {
            invokeCreateHooks(vnode, insertedVnodeQueue)
            setScope(vnode)
          } else {
            registerRef(vnode)
            insertedVnodeQueue.push(vnode)
          }
        } // 将组件elm插入页面中
        // 返回第12步 createElm
        // 流程结束
© 版权声明
THE END
喜欢就支持一下吧
点赞 0 分享 收藏

评论 (0)

取消