Vue基础知识之Vue.component


官方定义

Vue.component( id, [definition] )

  • 参数

    • {string} id
    • {Function | Object} [definition]
  • 用法

    注册或获取全局组件。注册还会自动使用给定的 id 设置组件的名称

    // 注册组件,传入一个扩展过的构造器
    Vue.component(
        "my-component",
        Vue.extend({
            /* ... */
        })
    );
    
    // 注册组件,传入一个选项对象 (自动调用 Vue.extend)
    Vue.component("my-component", {
        /* ... */
    });
    
    // 获取注册的组件 (始终返回构造器)
    var MyComponent = Vue.component("my-component");
  • 参考组件

基本示例

这里有一个 Vue 组件的示例:

// 定义一个名为 button-counter 的新组件
Vue.component("button-counter", {
    data: function () {
        return {
            count: 0,
        };
    },
    template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>',
});

组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter>。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:

<div id="components-demo">
    <button-counter></button-counter>
</div>
new Vue({ el: "#components-demo" });

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

组件复用的时候,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。此时data 必须是一个函数。因此每个实例可以维护一份被返回对象的独立的拷贝。如果 Vue 没有这条规则,点击一个按钮就可能会像如下代码一样影响到其它所有实例

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册。至此,我们的组件都只是通过 Vue.component 全局注册的:

Vue.component("my-component-name", {
    // ... options ...
});

全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

全局注册

各个 UI 组件库里的组件,全局定义,每个页面都可以使用。

到目前为止,我们只用过 Vue.component 来创建组件:

Vue.component("my-component-name", {
    // ... 选项 ...
});

这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。比如:

Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })

new Vue({ el: '#app' })
<div id="app">
    <component-a></component-a>
    <component-b></component-b>
    <component-c></component-c>
</div>

在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用。

局部注册

使用 vue 脚手架搭建的 vue 项目,我们按需引用子组件。

全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。

在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:

var ComponentA = {
    /* ... */
};
var ComponentB = {
    /* ... */
};
var ComponentC = {
    /* ... */
};

然后在 components 选项中定义你想要使用的组件:

new Vue({
    el: "#app",
    components: {
        "component-a": ComponentA,
        "component-b": ComponentB,
    },
});

对于 components 对象中的每个property来说,其property名就是自定义元素的名字,其property值就是这个组件的选项对象。

注意局部注册的组件在其子组件中不可用。例如,如果你希望 ComponentAComponentB 中可用,则你需要这样写:

var ComponentA = {
    /* ... */
};

var ComponentB = {
    components: {
        "component-a": ComponentA,
    },
    // ...
};

或者如果你通过Babelwebpack使用ES2015模块,那么代码看起来更像:

import ComponentA from "./ComponentA.vue";

export default {
    components: {
        ComponentA,
    },
    // ...
};

注意在 ES2015+ 中,在对象中放一个类似 ComponentA 的变量名其实是 ComponentA: ComponentA 的缩写,即这个变量名同时是:

  • 用在模板中的自定义元素的名称
  • 包含了这个组件选项的变量名

模块系统

在模块系统中局部注册

诸如你使用Babelwebpack的模块系统。在这些情况下,我们推荐创建一个 components 目录,并将每个组件放置在其各自的文件中。

然后你需要在局部注册之前导入每个你想使用的组件。例如,在一个假设的 ComponentB.jsComponentB.vue 文件中:

import ComponentA from "./ComponentA";
import ComponentC from "./ComponentC";

export default {
    components: {
        ComponentA,
        ComponentC,
    },
    // ...
};

现在 ComponentAComponentC 都可以在 ComponentB 的模板中使用了。

基础组件的自动化全局注册

可能你的许多组件只是包裹了一个输入框或按钮之类的元素,是相对通用的。我们有时候会把它们称为基础组件,它们会在各个组件中被频繁的用到。

所以会导致很多组件里都会有一个包含基础组件的长列表:

import BaseButton from "./BaseButton.vue";
import BaseIcon from "./BaseIcon.vue";
import BaseInput from "./BaseInput.vue";

export default {
    components: {
        BaseButton,
        BaseIcon,
        BaseInput,
    },
};

而只是用于模板中的一小部分:

<BaseInput v-model="searchText" @keydown.enter="search" />
<BaseButton @click="search">
    <BaseIcon name="search" />
</BaseButton>
  1. 组件全部导入后,批量注册
import dgDialog from "@/components/dgDialog/index.vue";
import svgIcon from "@/components/svgIcon/index.vue";
const allComponent = { dgDialog, svgIcon };
export default {
    install(app) {
        console.log(app, "app"); //App.vue
        Object.keys(allComponent).forEach((key) => {
            console.log(key, "key"); //dgDialog svgIcon
            app.component(key, allComponent[key]);
        });
    },
};

// 或是
const ComponentRegister = {
    install(Vue) {
        console.log(Vue, "Vue"); //App.vue
        Object.keys(allComponent).forEach((key) => {
            console.log(key, "key");
            Vue.component(key, allComponent[key]);
        });
    }
}

export default ComponentRegister;
  1. 使用webpack(或在内部使用了webpackVue CLI 3+)全局注册组件

使用 require.context方法引入组件

// 所有组件放在一个`components`文件夹内的写法
const ComponentRegister = {
    install(Vue) {
        // 文件结构为components/组件名称.vue
        const requireComponent = require.context(
            // 其组件目录的相对路径
            "@/components",
            // 是否查询其子目录
            false,
            // 匹配基础组件文件名的正则表达式
            /\.(vue|js)$/
        );

        requireComponent.keys().forEach((fileName) => {
            // 获取组件配置
            const componentConfig = requireComponent(fileName);

            // 获取当前组件的文件名称 vue文件中声明的name属性 || 文件名称
            const componentName =
                componentConfig.default.name ||
                fileName
                    .split("/")
                    .pop()
                    .replace(/\.\w+$/, ""); //或 fileName.replace(/^\.\/(.*)\.\w+$/, "$1");
            // 全局注册组件
            Vue.component(
                componentName,
                // 如果这个组件选项是通过 `export default` 导出的,
                // 那么就会优先使用 `.default`,
                // 否则回退到使用模块的根。
                componentConfig.default || componentConfig
            );
        });
    },
};

export default ComponentRegister;
// 组件以文件夹名称命名
const ComponentRegister = {
    install(Vue) {
        // 文件结构为文件夹名称(即组件名称)/index.vue
        const requireComponent = require.context(
            // 其组件目录的相对路径
            "@/components/",
            // 是否查询其子目录
            true,
            // 匹配基础组件文件名的正则表达式
            /\.(vue|js)$/
        );
        requireComponent.keys().forEach((fileName) => {
            // 获取组件配置
            const componentConfig = requireComponent(fileName);

            //获取当前组件的文件名称 vue文件中声明的name属性 || 文件名称
            const componentName =
                componentConfig.default.name ||
                fileName
                    .replace(/^\.\/(.*)\.\w+$/, "$1")
                    .split("/")
                    .shift(); //移除数组第一个元素  结果为 文件夹名称
            // .pop()//移除最后一个数组元素 结果为index

            // 全局注册组件
            Vue.component(
                componentName,
                // 如果这个组件选项是通过 `export default` 导出的,
                // 那么就会优先使用 `.default`,
                // 否则回退到使用模块的根。
                componentConfig.default || componentConfig
            );
        });
    },
};

export default ComponentRegister;
  1. 使用vite方法全局注册组件

使用import.meta.glob方法引入组件

const ComponentRegister = {
    install(Vue) {
        //获取所有的vue文件
        const requireComponent = import.meta.glob("@/components/*/*.vue", {
            // import: 'default',
            // The glob option "as" has been deprecated in favour of "query". Please update `as: 'component'` to `query: '?component'`.
            // as: "component",
            query: '?component',
            eager: true,
        });

        /**
         * (\/[^\/]+)+ 匹配一个或多个以/开头,后面跟着一个或多个非/字符的序列。这对应于路径中的目录名。
         * \/ 匹配/字符,它是目录名与我们要提取的部分之间的分隔符。
         * ([^\/]+) 匹配一个或多个非/字符,这就是我们要提取的部分(文件夹名称)。
         * \/ 再次匹配/字符,表示提取部分的结束和下一个目录或文件的开始。
         * */
        const re = /(\/[^\/]+)+\/([^\/]+)\//; //或是 /\/components\/(.*?)\//;  i.match(re)[1];
        for (const i in requireComponent) {
            //获取当前遍历的组件
            const componentName = i.match(re)[2];
            const componentConfig = requireComponent[i];
            Vue.component(componentName, componentConfig.default || componentConfig);
        }
    },
};

export default ComponentRegister;

记住全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生。如下

//注册组件到全局
Vue.component("Profile", Profile);
//创建vue
var vue = new Vue({
    el: "#app",
    data: {
        msg: "Vue是最简单的",
    },
});

文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录