Vue源码分析之vm对象

这是我参与更文挑战的第16天,活动详情查看: 更文挑战

前言

在阅读Vue源码的时候,经常会看到一个vm对象,里边有很多属性,并且伴随着各种属性操作。虽然知道这个vm就是当前的Vue实例,但是对源码不清晰的我来说看着看着就晕了,哪个属性是哪个,完全不记得了。这篇文章主要是来整理下这个vm对象,看看它里边的几个常用属性都是什么含义。

先来看下常遇到的vm对象的定义:

vm定义

直接将当前this赋值给vm

const vm: Component = this
复制代码

通过传参进来

function initInternalComponent (vm: Component, options: InternalComponentOptions) 
复制代码

从watcher中获得(Watcher构造函数中有一个属性是vm)

const vm = watcher.vm
复制代码

简单看下Watcher构造函数中对vm的定义:

export default class Watcher{
    vm: Component;
    ...
    
    constructor(vm: Component){
        this.vm = vm;
        if (isRenderWatcher) {
            vm._watcher = this
        }
        vm._watchers.push(this)
       
       ...
    }
    
    ...
}
复制代码

vm属性介绍

从定义处已经看到vm是一个VueComponet,即Vue组件。

看一个具体的Vue组件例子:

<template>
    <div id="example" @click="changeMessage">
        {{ message }}

        <no-rights />
    </div>
</template>

<script>
import noRights from '@/common/components/noRights';
export default {
    name: 'Test',
    components: {
        noRights
    },
    data() {
        return {
            message: '测试vm'
        };
    },

    watch: {
        message() {
            console.log('message变了');
        }
    },
    mounted() {
        window.vm = this;
    },
    methods: {
        changeMessage() {
            this.message = 'value改变了';
        }
    }
};
</script>

复制代码

对应的vm对象返回:

image.png

$el

当前Vue实例的真实DOM节点

$options

先来看下$options的定义:

// merge options
if(options && options._isComponent){
    ...
    const opts = vm.$options = Object.create(vm.constructor.options)
}else {
    vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
    )
}
复制代码

在前边介绍new Vue实例的时候看到过$options,它是Vue构造函数与Vue实例的options合并后的结果。

$parent

当前实例的父Vue实例

$children

当前实例的子组件(在这个例子中就是componets中的rights组件)

$root

当前组件树的根Vue实例。如果没有父实例,则vm.$root就指向自己

$vnode

当前Vue实例的父虚拟节点。如果为null,表示当前是根Vue的实例

vm.$vnode = parentVnode // update vm's placeholder node without re-render
复制代码

_vnode

当前Vue实例的虚拟节点,_vnode有个parent属性又指向实例的父虚拟节点,即$vnode

vm._vnode = vnode

if (vm._vnode) { // update child tree's parent
  vm._vnode.parent = parentVnode
}
复制代码

_self

当前Vue实例自己

vm._self = vm
复制代码

_uid

唯一的标志id,在调用Vue实例的私有方法_init时递增

let uid = 0;

Vue.prototype._init = function(Vue: Class<Component>) {
    const vm: Component = this;
    vm._uid = uid ++;
    
    ...
}
复制代码

_isVue

该属性也是在_init方法中定义的,作者给了解释,大概是说这个是为了防止this被observed

// a flag to avoid this being observed
vm._isVue = true
复制代码

来看下observe关于这块的定义:

export function observe (value: any, asRootData: ?boolean): Observer | void {
  ...
  
  let ob: Observer | void
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
  }
  ...
}

复制代码

从代码里可以看到如果_isVue为true,是不会去新建Observer实例的,即如果是Vue实例本身是不会去构造响应式的。

_isMounted

实例是否完成挂载,$mount的时候判断是根Vue实例时,设置为true

if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享