new Vue({})初始化流程 - Go语言中文社区

new Vue({})初始化流程


new Vue的初始化流程

new Vue({})之后发生了什么?

首先来看Vue的构造函数,

一、src/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)
}

可以看到这里会进入到_init()这个方法中。即Vue.prototype._init,所在文件为src/core/instance/init.js

二、src/core/instance/init.js

可以在这个文件中看到,_init()方法定义了很多的初始化方法, _init()源码:

export function initMixin(Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

    ....中间代码忽略,从45行看起
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)    
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm)    //声明$parent,$root,$parent,$refs
    initEvents(vm)       //对父组件传入的事件添加监听,事件是谁创建谁监听,子组件创建事件子组件监听
    initRender(vm)       //声明$slots和$createElement()
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props   获取注入数据
    initState(vm)   //(重要)数据响应式:初始化状态:props,methods,data,computed,watch
    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)
    }
  }
}

2.1 我们来描述一下这些初始化方法:

  1. initProxy:作用域代理,拦截组件内访问其它组件的数据。
  2. initLifecycle:建立父子组件关系,在当前组件实例上添加一些属性和生命周期标识。如parent,parent,refs,$children,_isMounted等。
  3. initEvents:对父组件传入的事件添加监听,事件是谁创建谁监听,子组件创建事件子组件监听
  4. initRender:声明slotsslots和createElement()等。
  5. initInjections:注入数据,初始化inject,一般用于组件更深层次之间的通信。
  6. initState:重要)数据响应式:初始化状态。很多选项初始化的汇总:data,methods,props,computed和watch。
  7. initProvide:提供数据注入。**思考:为什么先注入再提供呢??**答:1、首先来自祖辈的数据要和当前实例的data,等判重,相结合,所以注入数据的initInjections一定要在InitState的上面。2、从上面注入进来的东西在当前组件中转了一下又提供给后代了,所以注入数据也一定要在上面。
  8. vm.mount(vm.mount(vm.options.el):挂载实例。

三、接下来再从_init()方法的initLifecycle()开始初始化生命周期。

mountComponent: src/core/instance/lifecircle.js

执行挂载:在这个文件中获取VDom并转换为真实DOM。
首先在lifecircle.js文件中,定义了Vue.prototype._update 方法,这个方法会获取Vdom,接着执行mountComponent,然后执行vm._update(vnode, hydrating),源码如下:

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

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      vm._update(vnode, hydrating)  //把虚拟Dom转化为真实dom
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating)   //执行render() 获取vdom并渲染dom
    }
  }

四、接着在updateComponent这个方法中执行vm._update()方法会进入src/core/instance/render.js文件 中

Vue.prototype._render = function (): VNode {
    const vm: Component = this
    const { render, _parentVnode } = vm.$options

    if (_parentVnode) {
      vm.$scopedSlots = normalizeScopedSlots(
        _parentVnode.data.scopedSlots,
        vm.$slots,
        vm.$scopedSlots
      )
    }

    // set parent vnode. this allows render functions to have access
    // to the data on the placeholder node.
    vm.$vnode = _parentVnode
    // render self
    let vnode
    try {
      // There's no need to maintain a stack because all render fns are ui4op[poiuy]
      // separately from one another. Nested component's render fns are called
      // when parent component is patched.
      currentRenderingInstance = vm
      //这里把vdom转化为了真实dom
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } 

通过这句vnode = render.call(vm._renderProxy, vm.$createElement)把vdom替换成了真实dom。

始始化流程总结

new Vue() (vue构造函数) => _init() (初始化options中的各种属性,data,methods,props,computed,watch)=> $mount (调用mountComponent())===> mountComponent() (创建updateComponent()) ===> updateComponent() (执行_render()和_update()) ===> _render() (获取虚拟dom) ===> update() (把获取的虚拟dom转化为真实dom)

这里有一个例子来说明:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src='../../dist/vue.js'></script>
</head>

<body>
  <div id="app">
    <h2>数据初始化</h2>
    <p>{{foo}}</p>
  </div>
  <script>
    new Vue({
      el: '#app',
      data() {
        return {
          foo: 'fooooooo'
        }
      },
    })
  </script>

</body>

</html>

在浏览器中打开这个文件,断点设置在new Vue({})这里,F11进入到这个构造函数内部。

调试过程如下:

  1. 在这里插入图片描述
  2. 在这里插入图片描述
  3. 在这里插入图片描述
  4. List item
  5. List item
  6. List item
  7. List item
  8. List item
  9. 步骤8中的执行完成,界面上的{{foo}}就变成了foo,至此初始化过程就完成了。
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/sunny327/article/details/104391390
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢