zoukankan      html  css  js  c++  java
  • 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,最后返回"子类"(构造器),这个子类构造器。

  • 相关阅读:
    制作USB系统盘
    01Mysql 配置方法
    Tec_010_怎样看K线图
    回顾5年内的央行加息历史
    推荐:微软下一代操作系统Windows 7版本详解
    关于USB启动盘制作
    Delphi Program test
    圣诞节 玩具
    敏捷宣言
    [转 TDD] 如何坚持TDD:使用者出现的问题以及解决方案
  • 原文地址:https://www.cnblogs.com/dshvv/p/15692555.html
Copyright © 2011-2022 走看看