Vue2 组件通讯(四):v-model、scoped样式、mixins与plugins

在前三篇文章中,我们学习了Props、ref、自定义事件、全局事件总线、PubSub、插槽等多种组件通信方式。今天我们来学习最后几个重要的知识点:v-model在组件间的使用样式作用域混合(mixins) ​ 和插件(plugins)。掌握这些内容,你的Vue2知识体系将更加完整。

一、v-model双向数据绑定

1.1 基础用法

v-model 是Vue中最常用的指令之一,用于在表单元素上实现双向数据绑定

复制代码
<template>
    <div>
        <!-- 直接使用 v-model 实现双向数据绑定 -->
        <input type="text" v-model="keyword">
        <h3>当前输入的关键字:{{ keyword }}</h3>
    </div>
</template>

<script>
export default {
    data() {
        return {
            keyword: ''
        };
    }
};
</script>

1.2 v-model的实现原理

v-model 本质上是语法糖 ,它背后是由 :value@input 组合而成的。

等价写法一:使用方法和事件

复制代码
<template>
    <div>
        <!-- v-model的等价写法 -->
        <input type="text" :value="keyword" @input="changeKey">
        <h3>当前输入的关键字:{{ keyword }}</h3>
    </div>
</template>

<script>
export default {
    data() {
        return {
            keyword: ''
        };
    },
    methods: {
        changeKey(e) {
            this.keyword = e.target.value;
        }
    }
};
</script>

等价写法二:直接赋值

复制代码
<template>
    <div>
        <!-- 更简洁的等价写法 -->
        <input type="text" :value="keyword" @input="keyword = $event.target.value">
        <h3>当前输入的关键字:{{ keyword }}</h3>
    </div>
</template>

<script>
export default {
    data() {
        return {
            keyword: ''
        };
    }
};
</script>

v-model的本质

复制代码
v-model="keyword"
    ↓ 等价于
:value="keyword" + @input="keyword = $event.target.value"

二、组件间的v-model

2.1 在组件上使用v-model

v-model 不仅可以用于表单元素,还可以用在自定义组件上,实现父子组件之间的双向数据绑定。

父组件中使用v-model

复制代码
<template>
    <div id="app">
        <h3>父组件keyword:{{ keyword }}</h3>
        <!-- 在组件上使用 v-model -->
        <girl v-model="keyword"/>
    </div>
</template>

<script>
import girl from './components/girl.vue';

export default {
    name: 'App',
    data() {
        return {
            keyword: ''
        };
    },
    components: { girl }
};
</script>

2.2 组件v-model的实现原理

组件上的 v-model 同样是语法糖,等价于:

复制代码
<!-- v-model的等价写法 -->
<girl :value="keyword" @input="inputFun"/>

<script>
methods: {
    inputFun(val) {
        this.keyword = val;
    }
}
</script>

2.3 子组件实现v-model

子组件需要做两件事:

  1. 通过 props 接收 value
  2. 通过 $emit 触发 input 事件

子组件(girl.vue)

复制代码
<template>
    <div class="girl">
        <h3>子组件keyword:{{ value }}</h3>
        <!-- 输入框的值绑定props接收的value -->
        <input type="text" :value="value" @input="sendKeyword">
    </div>
</template>

<script>
export default {
    name: 'girl',
    // 默认接收 value 值
    props: ['value'],
    methods: {
        sendKeyword(e) {
            // 默认触发 input 事件,传递输入框的值
            this.$emit('input', e.target.value);
        }
    }
};
</script>

2.4 v-model在组件中的完整流程

复制代码
用户在子组件输入框中输入内容
    ↓
触发 @input 事件
    ↓
调用 sendKeyword(e) 方法
    ↓
this.$emit('input', e.target.value)
    ↓
父组件接收到 input 事件,更新 keyword 数据
    ↓
keyword 更新后,通过 :value 传递给子组件
    ↓
子组件展示最新的 value 值

2.5 组件v-model的核心约定

约定 说明
props名称 默认为 value
事件名称 默认为 input
数据流向 父→子::value;子→父:$emit('input')

如果需要修改默认的prop名称和事件名称,可以使用 model 选项,这部分内容将在后续文章中介绍。

三、样式作用域(scoped)

3.1 样式冲突问题

在组件化开发中,如果不加限制,组件的样式会互相影响

问题场景

复制代码
<!-- Girl.vue -->
<template>
    <div class="girl">
        <h2 class="title">girl组件</h2>
    </div>
</template>

<style>
/* 不加scoped,这个样式会影响所有组件 */
.title {
    background-color: blueviolet;
}
</style>

<!-- Son.vue -->
<template>
    <div class="son">
        <h2 class="title">son组件</h2>
    </div>
</template>

如果两个组件都有 .title 类名,不加 scoped 会导致样式污染。

3.2 scoped解决方案

使用 scoped 属性可以让样式只作用于当前组件

复制代码
<template>
    <div class="girl">
        <h2 class="title">girl组件</h2>
    </div>
</template>

<style scoped>
/* 这里的样式只作用于当前组件 */
.title {
    background-color: blueviolet;
    color: white;
    padding: 10px;
}
</style>

3.3 scoped的原理

当给 <style> 加上 scoped 属性后,Vue会自动做两件事:

  1. 给组件中的所有HTML元素添加一个唯一的属性 (如 data-v-7ba5bd90
  2. 修改CSS选择器,添加对应的属性选择器

编译前

复制代码
<style scoped>
.title {
    color: red;
}
</style>

编译后

复制代码
.title[data-v-7ba5bd90] {
    color: red;
}

生成的HTML

复制代码
<h2 class="title" data-v-7ba5bd90>girl组件</h2>

这样就保证了样式只会作用于当前组件的元素。

3.4 使用建议

场景 建议
组件内部样式 ✅ 使用 scoped
全局公共样式 App.vue 或单独的文件中定义,不加 scoped
覆盖第三方组件样式 使用深度选择器 /deep/>>>

四、混合(mixins)

4.1 什么是mixins?

mixins(混合) ​ 是一种分发Vue组件中可复用功能的方式。当多个组件有相同的配置项(如data、methods、生命周期等)时,可以将这些公共部分提取出来,通过mixins混入到各个组件中。

4.2 定义混合对象

mixins/handlerMsg.js

复制代码
export const handlerMsg = {
    data() {
        return {
            num: 123,
            commonData: '公共数据'
        };
    },
    methods: {
        getMsg() {
            console.log('来自mixins的方法');
            return this.num;
        },
        showCommon() {
            alert('这是公共方法');
        }
    },
    mounted() {
        console.log('mixins的mounted执行了');
    }
};

4.3 局部使用混合

在需要的组件中引入并使用:

复制代码
<template>
    <div class="girl">
        <h2>girl组件</h2>
        <h3>来自mixins的num:{{ num }}</h3>
        <button @click="getMsg">调用mixins方法</button>
    </div>
</template>

<script>
import { handlerMsg } from '../mixins/handlerMsg.js';

export default {
    name: 'girl',
    // 使用混合
    mixins: [handlerMsg],
    data() {
        return {
            girlMsg: 'girl组件自己的数据'
        };
    }
};
</script>

4.4 全局注册混合

main.js 中全局注册,所有组件都会拥有混合中的内容:

复制代码
// main.js
import Vue from 'vue';
import App from './App.vue';
import { handlerMsg } from './mixins/handlerMsg.js';

// 全局注册混合
Vue.mixin(handlerMsg);

new Vue({
    render: h => h(App)
}).$mount('#app');

4.5 混合的合并策略

当混合对象和组件本身有相同的配置项时,Vue有一套合并规则:

配置项 合并策略 示例
data 合并,冲突时以组件为准 混合有 num: 123,组件有 num: 456 → 结果为 456
methods 合并,冲突时以组件为准 同名方法,组件的会覆盖混合的
生命周期钩子 都会执行,混合的先执行 先执行混合的 mounted,再执行组件的 mounted

示例验证

复制代码
// 混合对象
const mixin = {
    data() {
        return {
            num: 123,
            mixinData: '混合数据'
        };
    },
    methods: {
        showMsg() {
            console.log('混合的showMsg');
        }
    },
    mounted() {
        console.log('混合的mounted');
    }
};

// 组件
export default {
    mixins: [mixin],
    data() {
        return {
            num: 456,  // 覆盖混合的num
            compData: '组件数据'
        };
    },
    methods: {
        showMsg() {  // 覆盖混合的showMsg
            console.log('组件的showMsg');
        }
    },
    mounted() {
        console.log('组件的mounted');
    }
};

// 结果:
// data: { num: 456, mixinData: '混合数据', compData: '组件数据' }
// methods: showMsg → 输出"组件的showMsg"
// mounted执行顺序:先输出"混合的mounted",再输出"组件的mounted"

4.6 mixins的使用场景

场景 说明
公共数据 多个组件共用的数据
公共方法 多个组件共用的方法
公共生命周期逻辑 多个组件都需要在某个生命周期执行相同的操作
公共计算属性 多个组件共用的计算属性

五、插件(plugins)

5.1 什么是插件?

插件 是用来给Vue做功能增强 的。一般情况下,我们自己去创建插件的可能性不大,但在实际开发中,有很多已经封装好的插件可以直接拿来使用,例如:vuexvue-router 等。

5.2 使用插件的步骤

使用插件通常分为三步:

  1. 导入插件

  2. 注册插件Vue.use(plugin)

  3. 使用插件提供的功能

    // 第一步:导入插件
    import Vuex from 'vuex';
    import VueRouter from 'vue-router';

    // 第二步:注册插件
    Vue.use(Vuex);
    Vue.use(VueRouter);

    // 第三步:使用插件提供的功能
    const store = new Vuex.Store({ /* ... / });
    const router = new VueRouter({ /
    ... */ });

    new Vue({
    store,
    router,
    render: h => h(App)
    }).$mount('#app');

5.3 定义自己的插件

插件是一个对象 ,对象中必须有 install 方法,这个方法会在 Vue.use() 时被自动调用。

plugins.js

复制代码
// 每一个插件都是一个对象
// 每一个插件对象中必须有一个install方法,这个install方法会被自动调用
// install方法中的参数包括两个部分:
// 第一部分:Vue构造函数
// 第二部分:可以接受用户在使用这个插件时传过来的数据,参数个数不限制

const plugins1 = {
    install(Vue, x, y, z) {
        console.log('插件install方法被调用了');
        console.log('Vue构造函数:', Vue);
        console.log('传递的参数:', x, y, z);
        
        // 可以在install方法中做一些全局配置
        // 例如:注册全局组件
        Vue.component('my-component', {
            template: '<h3>通过插件注册的全局组件</h3>'
        });
        
        // 例如:给Vue原型添加方法
        Vue.prototype.$myMethod = function() {
            console.log('通过插件添加的全局方法');
        };
        
        // 例如:注册全局指令
        Vue.directive('my-directive', {
            bind(el, binding) {
                el.style.color = binding.value;
            }
        });
    }
};

export default plugins1;

5.4 使用自定义插件

main.js

复制代码
import Vue from 'vue';
import App from './App.vue';
import plugins1 from './plugins/plugins.js';

// 使用插件,可以传递参数
Vue.use(plugins1, '参数1', '参数2', '参数3');

new Vue({
    render: h => h(App)
}).$mount('#app');

5.5 插件中可以做什么?

在插件的 install 方法中,可以做很多事情:

功能 代码示例
注册全局组件 Vue.component('comp-name', { ... })
注册全局指令 Vue.directive('dir-name', { ... })
添加全局方法/属性 Vue.prototype.$method = function() {}
添加全局mixin Vue.mixin({ ... })
添加实例方法 Vue.prototype.$myMethod = function() {}

5.6 常用Vue插件

插件 作用 安装
vuex 状态管理 npm install vuex
vue-router 路由管理 npm install vue-router
axios HTTP请求 npm install axios
element-ui UI组件库 npm install element-ui
vant 移动端UI库 npm install vant

六、核心概念总结

6.1 本章知识点一览

知识点 核心要点 适用场景
v-model :value + @input 的语法糖 表单双向绑定、父子组件双向数据绑定
组件v-model 子组件接收 value,触发 input 事件 封装可双向绑定的自定义组件
scoped 给样式添加唯一属性选择器 防止组件样式冲突
mixins 抽取公共配置项,分发到多个组件 多个组件共享data/methods/生命周期
plugins 通过 install 方法增强Vue功能 全局组件、指令、方法的注册

6.2 最佳实践建议

场景 推荐方案
表单组件封装 使用组件v-model实现双向绑定
组件样式隔离 始终使用 scoped
公共逻辑复用 使用mixins抽取公共部分
全局功能增强 封装为插件,通过 Vue.use() 注册
状态管理 使用Vuex插件

6.3 注意事项清单

  • ✅ v-model在组件上默认使用 valueinput,可通过 model 选项自定义
  • ✅ scoped样式中,子组件的根元素可以被子组件的样式影响
  • ✅ mixins的生命周期钩子会先于组件的生命周期钩子执行
  • ✅ 插件中的 install 方法接收的第一个参数是 Vue 构造函数,不是实例
  • ✅ 全局注册mixins会影响所有组件,谨慎使用

至此,Vue2组件通讯系列的四篇文章已经全部完成。从基础的Props到高级的事件总线、插槽、v-model、mixins和plugins,你已经掌握了Vue2更加完整的组件通信知识体系。

相关推荐
梦曦i1 小时前
@meng-xi/uni-router 未来展望:夯实基础、深化体验、探索前沿
前端·uni-app
前端炒粉1 小时前
DOM 常用 API 速查表
开发语言·javascript·ecmascript
Yeyu1 小时前
AAOS AppCard 实践:怎么把自己的 App 塞进别人的卡片里
前端
星云API技术支持1 小时前
企业微信二次开发:如何建立统一的错误处理、日志与请求追踪机制
javascript·企业微信·ruby
IT_陈寒2 小时前
Vite静态资源路径这个大坑害我调了一下午
前端·人工智能·后端
武哥聊编程2 小时前
【AI实战项目】基于SpringAI+Springboot+Vue的AI面试刷题训练平台
vue.js·人工智能·spring boot·springai
狗哥哥2 小时前
分享下我的读书清单
前端
合天网安实验室2 小时前
Log4J2 FilteredObjectInputStream RCE 漏洞分析
前端·黑客
两点王爷2 小时前
使用 GeoServer 发布 SHP 数据并在前端页面加载显示
前端