vue中的一个组件就是一个vue实例吗?

所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象

先说结论

这句话,这是官方文档说的
不过严谨来说,应该是

一个单页应用就是一个 vue 的实例
每个自定义组件就是一个 vueComponent 实例,

只不过 vueComponent 的构造器和 vue 的构造器的内容是基本一样的。效果表现也一致,两者对比如下

<div id="app">{{uname}}</div>
<script>
  // 仅有Vue实例
  var app = new Vue({
    el: "#app",
    data() {
      return {
        uname: "小红",
      };
    },
    methods: {
      say() {
        console.log("调用了say方法");
      },
    },
    mounted() {
      this.say();
    },
  });
</script>
<div id="app"></div>
<script>
  // 组件
  const RootCmp = {
    data() {
      return {
        uname: "小红",
      };
    },
    methods: {
      say() {
        console.log("调用了say方法");
      },
    },
    mounted() {
      this.say();
    },
    template: "<div>{{uname}}</div>",
  };

  var app = new Vue({
    el: "#app",
    render: (h) => h(RootCmp),
  });
</script>

证明

我们创建个 vue 项目

<div id="app">
  {{ message }}
  <cmp-one></cmp-one>
  ------
  <cmp-two></cmp-two>
</div>
<script>
  // 全局组件
  Vue.component("CmpOne", {
    template: "<div>组件1</div>",
  });
  var app = new Vue({
    el: "#app",
    data: {
      message: "Hello Vue!",
    },
    components: {
      CmpTwo: {
        // 局部组件
        template: "<div>组件2</div>",
      },
    },
  });
</script>

然后开始分析源码

以下是 vue.js 部分代码,
我们很快找到 Vue 的类,并添加个 log

function Vue(options) {
  console.log("Vue构造类");
  if (!(this instanceof Vue)) {
    warn("Vue is a constructor and should be called with the `new` keyword");
  }
  this._init(options);
}

结果发现,Vue 构造函数只会被执行一次,也就是你手动 new 的时候。
这足以证明每个组件(或.vue)文件不是 Vue 的实例。

那组件是谁的实例呢?

我们继续看源码,通过代码分析我们得知
全局组件 or 局部组件的注册初始化,是走这里

...
var Sub = function VueComponent (options) {
    console.log('VueComponent组件的构造类');
    this._init(options);
};
...
// 其中vnode.componentOptions.Ctor就是VueComponent类
new vnode.componentOptions.Ctor(options);

如上代码中,VueComponent组件的构造类会被打印两次。
这也就是说,它才是组件实例的构造函数(类)。

源码稍微深度分析

Vue.extend = function (extendOptions, is) {
  var Super = this;
  // ...
  var Sub = function VueComponent(options) {
    console.log("VueComponent组件的构造用来 实例化");
    this._init(options);
  };
  Sub.prototype = Object.create(Super.prototype);
  Sub.prototype.constructor = Sub;
  // ...
  return Sub;
};
function initAssetRegisters(Vue) {
  // ...
  ASSET_TYPES.forEach(function (type) {
    Vue[type] = function (id, definition) { 
      if (!definition) {
        return this.options[type + "s"][id];
      } else {
        if (type === "component" && isPlainObject(definition)) {
          definition.name = definition.name || id;
          definition = this.options._base.extend(definition);
        }
        return definition;
      }
    };
  });
}
function initGlobalAPI(Vue) {
  // ...
  initAssetRegisters(Vue);
}
initGlobalAPI(Vue);

解读:
项目初始化进来的时候会执行一个全局方法 initGlobalAPI
initGlobalAPI里会调用initAssetRegisters
initAssetRegisters的作用是给你定义的一些全局Asset扩展一些属性
枚举ASSET_TYPES 就是全局Asset:包含 component,directive,filter
比如我的组件 CmpOne,原本只有 template 属性,经过扩展后,就会多出如下属性

{
    cid: 1
    component: ƒ ( id, definition )
    directive: ƒ ( id, definition )
    extend: ƒ (extendOptions, is)
    extendOptions: {template: '<div>组件1</div>', name: 'CmpOne', _Ctor: {…}}
    filter: ƒ ( id, definition )
    mixin: ƒ (mixin)
    options: {components: {…}, directives: {…}, filters: {…}, template: '<div>组件1</div>', _base: ƒ, …}
    sealedOptions: {components: {…}, directives: {…}, filters: {…}, template: '<div>组件1</div>', _base: ƒ, …}
    super: ƒ Vue(options)
    superOptions: {components: {…}, directives: {…}, filters: {…}, _base: ƒ}
    use: ƒ (plugin)
}

其中重要的一点就是给CmpOne组件指定了构造器
那是怎么指定的呢?

this.options._base.extend(definition);

这句代码本质上就是调用了Vue.extend来创建了一个Vue的子类。
那么情况就明了了,组件构造函数是继承自Vue构造。
最后vue在创建vdom的时候,就会去实例化组件类,即new VueComponent()

总结

  • Vue组件构造类是继承自Vue类
  • 它们的属性options也基本是一样的,Vue 实例会比 Vue 组件多出 el 和 router 属性(对应官方说辞:组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。)
  • 所有的Vue组件都是Vue实例这句话只是官方为了方便咱们理解,但严格来说并不对,严格来说应该是所有的Vue组件都是Vue的子类实例
  • 在多页应用下或vue2的Vue.extend和vue3的createApp,才会形成多个Vue实例

扩展

Vue.component和Vue.extend的联系:
Vue.component注册或获取全局组件。注册还会自动使用给定的 id 设置组件的名称,如上id就是组件名CmpOne,内部实质上调用了Vue.extend,最后返回"子类"(构造器),这个子类构造器。

原文地址:https://www.cnblogs.com/dshvv/p/15692555.html