Maison  >  Article  >  interface Web  >  Que fait l'initialisation de vue ?

Que fait l'initialisation de vue ?

青灯夜游
青灯夜游original
2022-12-21 18:44:132876parcourir

Que faire : 1. Fusionner les options et traiter le contenu de configuration du composant ; 2. Initialiser les propriétés liées au cycle de vie de l'instance de vue ; 3. Initialiser la surveillance des événements du composant personnalisé ; 4. Initialiser les emplacements et les fonctions de rendu ; requis pour le rendu.etc.; 5. Appelez la fonction beforeCreate; 6. Initialisez les données injectées; 7. Initialisez les accessoires, les données, la montre, etc. 8. Initialisez provide après avoir injecté les données transmises par les ancêtres; la fonction créée ; 10. Montez l'élément DOM.

Que fait l'initialisation de vue ?

L'environnement d'exploitation de ce tutoriel : système windows7, version vue3, ordinateur DELL G3.

Créer une instance

Le fichier src/instance/index.js est le fichier d'entrée pour créer une instance Vue

Il y aura d'abord quelques méthodes de mixage avant la création pour initialiser les méthodes et propriétés de l'instance

initMixin (Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
Il suffit de regarder initMixin ici en premier

initMixin(Vue) initialisation mixin

la méthode initMixin consiste à mélanger Méthode _init de Vue, le cœur de la méthode _init est la méthode suivante_init方法,_init方法里的核心又是以下方法

  • 合并options配置
  • 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')

0. 合并options配置

  vm.$options = mergeOptions(  // 合并options
    resolveConstructorOptions(vm.constructor),
    options || {},
    vm
  )

合并options并在实例上挂载一个$options属性。合并什么东西了?这里是分两种情况的:

  • 初始化new Vue

    在执行new Vue构造函数时,参数就是一个对象,也就是用户的自定义配置;会将它和vue之前定义的原型方法,全局API属性;还有全局的Vue.mixin内的参数,将这些都合并成为一个新的options,最后赋值给一个的新的属性$options

  • 子组件初始化

    如果是子组件初始化,除了合并以上那些外,还会将父组件的参数进行合并,如有父组件定义在子组件上的eventprops等等。

经过合并之后就可以通过this.$options.data访问到用户定义的data函数,this.$options.name访问到用户定义的组件名称,这个合并后的属性很重要,会被经常使用到。

1. initLifecycle(vm)

主要作用是确认组件的父子关系和初始化某些实例属性。

export function initLifecycle(vm: Component) {
  const options = vm.$options  // 之前合并的属性
  
  let parent = options.parent;
  if (parent && !options.abstract) { //  找到第一个非抽象父组件
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }
  
  vm.$parent = parent  // 找到后赋值
  vm.$root = parent ? parent.$root : vm  // 让每一个子组件的$root属性都是根组件
  
  vm.$children = []
  vm.$refs = {}
  
  vm._watcher = null
  ...
  vm._isDestroyed = false
  vm._isBeingDestroyed = false
}

2. initEvents(vm)

初始化自定义组件事件的监听,若存在父监听事件,则添加到该实例上。 主要作用是将父组件在使用v-on或@注册的自定义事件添加到子组件的事件中心中。

export function initEvents (vm: Component) {
  vm._events = Object.create(null)  // 事件中心
  vm._hasHookEvent = false
  // init parent attached events
  const listeners = vm.$options._parentListeners // 经过合并options得到的
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }
}

3. initRender(vm)

初始化render渲染所需的slots、渲染函数等。其实就两件事。 1、插槽的处理、 2、$createElm 也就是 render 函数中的 h 的声明

export function initRender(vm) {
  vm._vnode = null
  ...
  vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)  //转化编译器的
  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)  // 转化手写的
  ...
}

4. callHook(vm, 'beforeCreate')

调用 beforeCreate

  • Configuration des options de fusion
  • initLifecycle(vm)initEvents(vm)
  • initRender(vm)
  • callHook(vm, 'beforeCreate')
  • initInjections(vm) // résolution injections avant les données/props
  • initState(vm)
  • initProvide(vm) // résolution de la fourniture après les données/props
  • callHook(vm, 'created')

0. Fusionner la configuration des options

export function initInjections (vm: Component) {
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    toggleObserving(false)  // 刻意为之不被响应式
    Object.keys(result).forEach(key => {
      ...
      defineReactive(vm, key, result[key])
    })
    toggleObserving(true)
  }
}

Fusionner les options et monter un attribut $options sur l'instance. Qu'est-ce qui a été fusionné ? Il y a deux situations ici :

  • Initialisation de la nouvelle VueLors de l'exécution du constructeur new Vue, le paramètre est un objet, qui est la configuration personnalisée de l'utilisateur. fusionné avec les méthodes prototypes et les propriétés globales de l'API définies avant vue ainsi qu'avec les paramètres du Vue.mixin global, et ceux-ci seront fusionnés dans un nouveau  ; options, et enfin assigné à un nouvel attribut $options.

  • Initialisation du sous-composant

    S'il s'agit d'une initialisation de sous-composant, en plus de fusionner ce qui précède, les paramètres du composant parent seront également fusionnés s'il y a un événement défini par le parent. composant sur le composant enfant , props, etc.

Après la fusion, vous pouvez accéder à la fonction de données définie par l'utilisateur via this.$options.data, this.$options.nameAccès au nom du composant défini par l'utilisateur. Cet attribut fusionné est très important et sera fréquemment utilisé. 🎜🎜

🎜🎜1. initLifecycle(vm)🎜🎜🎜🎜La fonction principale est de confirmer la relation parent-enfant du composant et d'initialiser certaines propriétés de l'instance. 🎜
export function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    // 首先定义一个result返回找到的结果。
    const result = Object.create(null)
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)
    // 接下来使用双循环查找,外层的for循环会遍历inject的每一项
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // #6574 in case the inject object is observed...
      if (key === &#39;__ob__&#39;) continue
      const provideKey = inject[key].from
      let source = vm
      //然后再内层使用while循环自底向上的查找inject该项的父级是否有提供对应的依赖。
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      if (!source) {
        if (&#39;default&#39; in inject[key]) {
          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === &#39;function&#39;
            ? provideDefault.call(vm)
            : provideDefault
        } else if (process.env.NODE_ENV !== &#39;production&#39;) {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }
    return result
  }
}

🎜🎜2. initEvents(vm)🎜🎜🎜🎜Initialisez l'écoute des événements de composant personnalisés S'il existe un événement d'écoute parent, il est ajouté à l'instance. La fonction principale consiste à ajouter des événements personnalisés enregistrés par le composant parent à l'aide de v-on ou @ au centre d'événements du composant enfant. 🎜
export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

🎜🎜3. initRender(vm)🎜🎜🎜🎜Initialisez les emplacements, les fonctions de rendu, etc. requis pour le rendu. En fait, ce ne sont que deux choses. 1. Traitement des fentes, 2. $createElm est la déclaration de h dans la fonction de rendu🎜
function initProps(vm: Component, propsOptions: Object) {  // 第二个参数为验证规则
  const propsData = vm.$options.propsData || {}  // props具体的值 父组件传过来的
  const props = vm._props = {}  // 存放props  组件内可以通过这个访问到传过来的props
  const isRoot = !vm.$parent // 是否是根节点
  if (!isRoot) {   // 不是根节点则关闭响应式
    toggleObserving(false)
  }
  for (const key in propsOptions) {
    const value = validateProp(key, propsOptions, propsData, vm)
    defineReactive(props, key, value)
    if (!(key in vm)) {
      proxy(vm, `_props`, key)  // 代理 this.xx实际上访问的是 this._props.xx
    }
  }
  toggleObserving(true)
}

🎜🎜4 callHook(vm, 'beforeCreate')🎜🎜🎜🎜Appelez beforeCreatecode> fonction crochet. Il vous suffit maintenant de savoir qu'il exécutera la méthode de cycle de vie définie par l'utilisateur, et s'il y a un mixin mélangé, il sera également exécuté. 🎜🎜🎜Question d'entretien connexe : les variables définies dans les données sont-elles accessibles via ce hook beforeCreate ? Pourquoi et que peut faire ce hook ? 🎜🎜🎜🎜> Réponse : Il n'est pas accessible car lors de la phase d'initialisation de la vue, les variables dans data n'ont pas encore été montées dessus, et la valeur d'accès à ce moment sera indéfinie. Le hook beforeCreate est rarement utilisé dans le développement commercial quotidien. Lorsque la méthode instanll à l'intérieur du plug-in est installée via la méthode Vue.use, elle est généralement exécutée dans le hook beforeCreate. C'est ainsi que vue-router et vuex procèdent. 🎜🎜🎜🎜5. initInjections(vm)🎜🎜🎜🎜La séquence d'initialisation suivante est inject => Initialiser inject avant props/data Le but de ceci est d'utiliser le contenu injecté dans inject dans props/data🎜<pre class="brush:js;toolbar:false;">function initMethods (vm: Component, methods: Object) { const props = vm.$options.props for (const key in methods) { if(methods[key] == null) { // methods[key] === null || methods[key] === undefined 的简写 warn(`只定义了key而没有相应的value`) } if(props &amp;&amp; hasOwn(props, key)) { warn(`方法名和props的key重名了`) } if((key in vm) &amp;&amp; isReserved(key)) { warn(`方法名已经存在而且以_或$开头`) } vm[key] = typeof methods[key] !== &amp;#39;function&amp;#39; ? noop : bind(methods[key], vm) // 相当于methods[key].bind(vm) } }</pre>🎜La fonction principale de solveInject est de trouver les données actuellement injectées couche par couche de bas en haut 🎜.<pre class="brush:js;toolbar:false;">export function resolveInject (inject: any, vm: Component): ?Object { if (inject) { // inject is :any because flow is not smart enough to figure out cached // 首先定义一个result返回找到的结果。 const result = Object.create(null) const keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject) // 接下来使用双循环查找,外层的for循环会遍历inject的每一项 for (let i = 0; i &lt; keys.length; i++) { const key = keys[i] // #6574 in case the inject object is observed... if (key === &amp;#39;__ob__&amp;#39;) continue const provideKey = inject[key].from let source = vm //然后再内层使用while循环自底向上的查找inject该项的父级是否有提供对应的依赖。 while (source) { if (source._provided &amp;&amp; hasOwn(source._provided, provideKey)) { result[key] = source._provided[provideKey] break } source = source.$parent } if (!source) { if (&amp;#39;default&amp;#39; in inject[key]) { const provideDefault = inject[key].default result[key] = typeof provideDefault === &amp;#39;function&amp;#39; ? provideDefault.call(vm) : provideDefault } else if (process.env.NODE_ENV !== &amp;#39;production&amp;#39;) { warn(`Injection &quot;${key}&quot; not found`, vm) } } } return result } }</pre><h3 data-id="heading-10"><span style="font-size: 18px;"><strong>6. initState(vm)</strong></span></h3> <p>初始化会被使用到的状态,状态包括props,methods,data,computed,watch五个选项。(这里先看props,methods,data)</p><pre class="brush:js;toolbar:false;">export function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch &amp;&amp; opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }</pre><h4 data-id="heading-11"><strong>6.1 initProps</strong></h4> <ul><li>主要作用是检测子组件接受的值是否符合规则,以及让对应的值可以用this直接访问</li></ul><pre class="brush:js;toolbar:false;">function initProps(vm: Component, propsOptions: Object) { // 第二个参数为验证规则 const propsData = vm.$options.propsData || {} // props具体的值 父组件传过来的 const props = vm._props = {} // 存放props 组件内可以通过这个访问到传过来的props const isRoot = !vm.$parent // 是否是根节点 if (!isRoot) { // 不是根节点则关闭响应式 toggleObserving(false) } for (const key in propsOptions) { const value = validateProp(key, propsOptions, propsData, vm) defineReactive(props, key, value) if (!(key in vm)) { proxy(vm, `_props`, key) // 代理 this.xx实际上访问的是 this._props.xx } } toggleObserving(true) }</pre><h4 data-id="heading-12"><strong>6.2 initMethods</strong></h4> <ul><li>主要作用是将methods内的方法挂载到this下。</li></ul><pre class="brush:js;toolbar:false;">function initMethods (vm: Component, methods: Object) { const props = vm.$options.props for (const key in methods) { if(methods[key] == null) { // methods[key] === null || methods[key] === undefined 的简写 warn(`只定义了key而没有相应的value`) } if(props &amp;&amp; hasOwn(props, key)) { warn(`方法名和props的key重名了`) } if((key in vm) &amp;&amp; isReserved(key)) { warn(`方法名已经存在而且以_或$开头`) } vm[key] = typeof methods[key] !== &amp;#39;function&amp;#39; ? noop : bind(methods[key], vm) // 相当于methods[key].bind(vm) } }</pre><h4 data-id="heading-13"><strong>6.3 initData</strong></h4><pre class="brush:js;toolbar:false;">function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === &amp;#39;function&amp;#39; ? getData(data, vm) : data || {} if (!isPlainObject(data)) { data = {} } // proxy data on instance const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (methods &amp;&amp; hasOwn(methods, key)) { warn(`和methods内的方法重名了`) } if (props &amp;&amp; hasOwn(props, key)) { warn(`和props内的key重名了`) } else if (!isReserved(key)) { // key不能以_或$开头 proxy(vm, `_data`, key) } } // observe data observe(data, true /* asRootData */) }</pre><h4 data-id="heading-14"><strong>initProvide(vm)</strong></h4> <p>很容易看出是把用户传进来的<code>provide选项先获取到,如果是方法则执行一下再挂载到实例的_provided属性,不是则直接挂载到_provided属性

export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === &#39;function&#39;
      ? provide.call(vm)
      : provide
  }
}

7.callHook(vm, 'created')

调用 created钩子函数

总结:Vue初始化都做了什么?

1、选项合并,处理组件的配置内容,将传入的options与构造函数本身的options进行合并(用户选项和系统默认的选项进行合并)

2、初始化vue实例生命周期相关的属性,定义了比如:root、root、root、parent、children、children、children、refs

3、初始化自定义组件事件的监听,若存在父监听事件,则添加到该实例上

4、初始化render渲染所需的slots、渲染函数等。其实就两件事:插槽的处理 和 $createElm的声明,也就是 render 函数中的 h 的声明

5、调用 beforeCreate 钩子函数,在这里就能看出一个组件在创建前和后分别做了哪些初始化

6、初始化注入数据,隔代传参时 先inject。作为一个组件,在要给后辈组件提供数据之前,需要先把祖辈传下来的数据注入进来

7、对props,methods,data,computed,watch进行初始化,包括响应式的处理

8、在把祖辈传下来的数据注入进来以后 再初始化provide

9、调用 created 钩子函数,初始化完成,可以执行挂载了

10、挂载到对应DOM元素上。如果组件构造函数设置了el选项,会自动挂载,所以就不用再手动调用$mount去挂载

【相关推荐:vuejs视频教程web前端开发

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn