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,最后返回"子類"(構造器),這個子類構造器。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM