一个项目是由很多组件组成的。而组件之间可能存在很多相似的功能,如果我们在每个组件中去重复定义这些功能,会使得项目出现代码冗余并提高了维护难度。针对这种情况官方提供了很多可复用解决方案,比如:自定义指令、Mixin、插件等。

1.自定义指令

除了核心功能默认内置的指令 (例如 v-model 和 v-show 等),Vue 也允许创建自己的指令。自己做的指令中可以封装某个功能,这样就能达到复用代码的目的。

1.1.全局指令

下面实例中自定义一个全局指令,能够显示当前时间。

  1. <body>
  2. <div id="app">
  3. <div v-date></div>
  4. <div v-date></div>
  5. </div>
  6. <script src="https://unpkg.com/vue@next"></script>
  7. <script>
  8. let app = Vue.createApp({});
  9. app.directive('date', {
  10. mounted(el) {
  11. el.innerHTML = new Date();
  12. }
  13. })
  14. //注意这里:需要先声明自定义指令后再绑定视图
  15. app.mount('#app');
  16. </script>
  17. </body>
  • 使用Vue实例中的directive函数创建自定义指令。第一个参数是指令名,第二个参数是指令内容。
  • 自定义指令中,使用了 mounted 生命周期,因为需要操作DOM。
  • mounted 生命周期函数中有一个参数,此参数就是使用指令的DOM对象。
  • 使用时指令依然为 “v-指令名” 的形式。
  • 上面自定义指令直接挂载到Vue实例上,所以是全局指令

1.2.局部指令

下面单独创建自定义指令,然后在Vue实例中引用这个指令,所以是局部指令。

<body>

    <div id="app">
        <div v-date></div>
        <div v-date></div>
    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script>
        const mydirective = {
            date: {
                mounted(el) {
                    el.innerHTML = new Date();
                }
            }
        }

        let app = Vue.createApp({
            data() {
                return {
                }
            },
            directives: mydirective
        });

        app.mount('#app');
    </script>
</body>
  • 上面代码中,单独创建一个自定义指令,所以需要在Vue实例中使用directives属性引用这个指令。

1.3.指令参数

指令作为封装好的一段功能代码,应该具有参数功能,这样才能更加灵活的封装代码。

下面实现一个带参数的自定义指令,它能够将图片按照指定大小显示成圆形图片。

<body>

    <div id="app">
        <img src="img/gf01.jpeg" v-imgcir="200">
    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script>
        const mydirective = {
            imgcir: {
                mounted(el,binding) {
                    el.style.width = binding.value+'px';
                    el.style.height = binding.value+'px';
                    el.style.borderRadius = (binding.value/2)+'px';
                }
            }
        }

        let app = Vue.createApp({
            data() {
                return {
                }
            },
            directives: mydirective
        });

        app.mount('#app');
    </script>
</body>
  • 首先,在视图层使用自定义指令时添加一个参数值。
  • 自定义指令可以使用binding来接收这个参数,并使用binding.value的形式获取。

2.Mixin混入

官方解释:Mixin 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个 mixin 对象可以包含任意组件选项。当组件使用 mixin 对象时,所有 mixin 对象的选项将被“混合”进入该组件本身的选项。

2.1.基本用法

<body>
    <div id="app">
        <p>num:{{num}}</p>
        <p>name:{{name}}</p>
    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script>
        const common = {
            data(){
                return {
                    name: '张三'
                }
            }
        }

        let app = Vue.createApp({
            data() {
                return {
                    num: 10
                }
            },
            mixins: [common]
        });
        app.mount('#app');
    </script>
</body>
  • 先定义共通内容。再使用mixins属性将此内容混入到Vue实例中。
  • 此时,Vue实例中就可以使用混入的内容了。

2.2.混入优先级

一个 mixin 中可以书写 Vue 中的任何选项,包括 data、methods、计算属性等等。那么,如果混入内容与Vue原有选项内容有冲突时如何处理呢?

<body>

    <div id="app">
        <p>num:{{num}}</p>
        <button @click="add">点击</button>
    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script>
        const common = {
            data(){
                return {
                    num: 20
                }
            },
            methods:{
                add(){
                    console.log('Mixin事件');
                }
            },
            created(){
                console.log('Mixin生命周期');
            }
        }

        let app = Vue.createApp({
            data() {
                return {
                    num: 10
                }
            },
            mixins: [common],
            methods:{
                add(){
                    console.log('Vue实例事件');
                }
            },
            created(){
                console.log('Vue生命周期');
            }
        });
        app.mount('#app');
    </script>
</body>

通过上面实例可以发现优先级规则:

  • 在mixin与组件选项有重叠时,组件选项优先级高于mixin选项。
  • 但生命周期函数会先执行mixin的生命周期函数,再执行组件的。

2.3.全局 Mixin

Mixin 也可以进行全局注册。也就是将Mixin直接挂载到Vue实例上,这样,组件中就不用再写mixins: [common]选项了。这就是全局Mixin。

<body>

    <div id="app">
        <p>num:{{num}}</p>
        <button @click="add">点击</button>
    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script>
        let app = Vue.createApp({
            data() {
                return {
                    num: 10
                }
            },
            //mixins: [common],
            methods:{
                add(){
                    console.log('Vue实例事件');
                }
            },
            created(){
                console.log('Vue生命周期');
            }
        });

        //将 Mixin直接挂载到Vue实例上
        app.mixin({
            data(){
                return {
                    num: 20
                }
            },
            methods:{
                add(){
                    console.log('Mixin事件');
                }
            },
            created(){
                console.log('Mixin生命周期');
            }
        });
        app.mount('#app');
    </script>
</body>
  • 将Mixin直接挂载到Vue实例上。
  • Vue中就不需要mixins选项了。

注意:一旦使用全局 mixin,它将影响每一个之后创建的组件 (例如,每个子组件)。

3.插件

plugin插件是把通用功能封装起来,通常向 Vue 添加全局级功能。比如,给Vue添加自定义指令、Mixin等。

3.1.基本用法

下面实例中,声明一个插件并被Vue实例所引用。

<body>

    <div id="app">

    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script>
        //对象形式声明插件
        const MyPlugin = {
            install(app, options) {
                console.log('hello world!');
                console.log(app);
                console.log(options);
            }
        }

        /*
        //函数形式声明插件
        const MyPlugin = (app, options) => {
            console.log('hello world!');
            console.log(app);
            console.log(options);
        }
        */

        let app = Vue.createApp({
            data() {
                return {
                }
            },
        });

        app.use(MyPlugin, { name: 'zhangsan' })

        app.mount('#app');
    </script>
</body>
  • 声明插件可以有两种形式:对象形式、函数形式。
  • 如果是对象形式,那么需要声明 install 方法,并自动给 install 方法传递两个参数。
  • 如果是函数形式,那么直接给此函数传递两个参数。
  • 第一个参数为Vue实例,第二个参数为引用插件时所传递的数据。
  • 使用时,调用Vue实例的use方法引入插件,并给插件传递数据(可选)。

3.2.给Vue扩展功能

下面在插件中给Vue扩展功能。

<body>

    <div id="app">
        <img src="img/gf01.jpeg" v-imgcir="200">
        <p>{{curdate}}</p>
    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script>
        const MyPlugin = {
            install(app, options) {
                app.directive('imgcir', {
                    mounted(el, binding) {
                        el.style.width = binding.value + 'px';
                        el.style.height = binding.value + 'px';
                        el.style.borderRadius = (binding.value / 2) + 'px';
                    }
                });
                app.mixin({
                    data(){
                        return {
                            curdate: new Date()
                        }
                    }
                });
            }
        }

        let app = Vue.createApp({
            data() {
                return {
                }
            },
        });

        app.use(MyPlugin)

        app.mount('#app');
    </script>
</body>
  • 上面代码中,通过插件给Vue扩展了一个自定义标签,一个Mixin。