赞
踩
Mixin
是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问mixin
类的方法而不必成为其子类
Mixin
类通常作为功能模块使用,在需要该功能时“混入”,有利于代码复用又避免了多继承的复杂
先来看一下官方定义
mixin
(混入),提供了一种非常灵活的方式,来分发Vue
组件中的可复用功能。
本质其实就是一个js
对象,它可以包含我们组件中任意功能选项,如data
、components
、methods
、created
、computed
等等
我们只要将共用的功能以对象的方式传入 mixins
选项中,当组件使用 mixins
对象时所有mixins
对象的选项都将被混入该组件本身的选项中来
在Vue
中我们可以局部混入跟全局混入
首先在 src 同级目录下 创建 mixin/index.js 文件
- export const mixins = {
- data() {
- return {
- msg: "我是mixin中的数据",
- };
- },
- computed: {},
- created() {
- console.log("我是 mixin 中的 created 生命周期函数");
- },
- mounted() {},
- methods: {
- geMes(){
- console.log("我是点击事件 hhhhh");
- }
- },
- };

在组件中使用
- <script>
- import { mixins } from "./mixin/index";
- export default {
- name: "app",
- mixins: [mixins],
- created() {
- console.log("我是app的生命周期 created",this.msg);
- this.geMes()
- }
- };
- </script>
结果
以上代码引入 mixin 的方法很简单,直接使用vue提供给我们的mixins 属性:mixins:[mixins]
总结:
那么多个组件使用相同的 mixin, 当mixin被修改了 其他组件数据会发生变化吗?
在 components中创建一个 demo 组件
代码如下
- // demo.vue
- <template>
- <div>
- <p>我是 demo 组件</p>
- <p>我使用了 mixin 中的数据 msg —————— {{ msg }}</p>
- </div>
- </template>
-
- <script>
- import { mixins } from "@/mixin/index";
- export default {
- name: "app",
- mixins: [mixins],
- }
- </script>
app组件
- // app.vue
- <template>
- <div>
- <!-- 组件 -->
- <demo/>
- <!-- app 本体内容 -->
- <div>
- 我是app页面的按钮 点击修改mixin中的msg
- <p>mixin —————— {{ msg }}</p>
- <button @click="setMsg">修改msg</button>
- </div>
- </div>
- </template>
-
- <script>
- import demo from './components/demo.vue';
- import { mixins } from "./mixin/index";
- export default {
- components: { demo },
- name: "app",
- mixins: [mixins],
- methods: {
- setMsg() {
- this.msg = "我被修改了!!!"
- }
- }
-
- };

结果
结论
在app组件更改了 msg,demo没有变化,所以 不同组件中的mixin 是相互独立的
在mian.js 挂载mixin就可以每个组件使用了
- import Vue from 'vue'
- import App from './App.vue'
- import { mixins } from "./mixin/index";
- Vue.mixin(mixins)
-
- Vue.config.productionTip = false
-
- new Vue({
- render: h => h(App),
- }).$mount('#app')
请谨慎使用全局混入,因为它会影响每个单独创建的 Vue 实例 (包括第三方组件)。大多数情况下,只应当应用于自定义选项,就像上面示例一样。推荐将其作为插件发布,以避免重复应用混入。
当mixin中定义的属性或方法的名称与组件中定义的名称发生冲突怎么办?
从源码上看 可以分为如下几种类型
替换型合并有props
、methods
、inject
、computed
- strats.props =
- strats.methods =
- strats.inject =
- strats.computed = function (
- parentVal: ?Object,
- childVal: ?Object,
- vm?: Component,
- key: string
- ): ?Object {
- if (!parentVal) return childVal // 如果parentVal没有值,直接返回childVal
- const ret = Object.create(null) // 创建一个第三方对象 ret
- extend(ret, parentVal) // extend方法实际是把parentVal的属性复制到ret中
- if (childVal) extend(ret, childVal) // 把childVal的属性复制到ret中
- return ret
- }
- strats.provide = mergeDataOrFn

同名的
props
、methods
、inject
、computed
会被后来者代替
和并型合并有:data
- strats.data = function(parentVal, childVal, vm) {
- return mergeDataOrFn(
- parentVal, childVal, vm
- )
- };
-
- function mergeDataOrFn(parentVal, childVal, vm) {
- return function mergedInstanceDataFn() {
- var childData = childVal.call(vm, vm) // 执行data挂的函数得到对象
- var parentData = parentVal.call(vm, vm)
- if (childData) {
- return mergeData(childData, parentData) // 将2个对象进行合并
- } else {
- return parentData // 如果没有childData 直接返回parentData
- }
- }
- }
-
- function mergeData(to, from) {
- if (!from) return to
- var key, toVal, fromVal;
- var keys = Object.keys(from);
- for (var i = 0; i < keys.length; i++) {
- key = keys[i];
- toVal = to[key];
- fromVal = from[key];
- // 如果不存在这个属性,就重新设置
- if (!to.hasOwnProperty(key)) {
- set(to, key, fromVal);
- }
- // 存在相同属性,合并对象
- else if (typeof toVal =="object" && typeof fromVal =="object") {
- mergeData(toVal, fromVal);
- }
- }
- return to

mergeData
函数遍历了要合并的 data 的所有属性,然后根据不同情况进行合并:
set
方法进行合并(set方法其实就是一些合并重新赋值的方法)队列性合并有:全部生命周期和watch
- function mergeHook (
- parentVal: ?Array<Function>,
- childVal: ?Function | ?Array<Function>
- ): ?Array<Function> {
- return childVal
- ? parentVal
- ? parentVal.concat(childVal)
- : Array.isArray(childVal)
- ? childVal
- : [childVal]
- : parentVal
- }
-
- LIFECYCLE_HOOKS.forEach(hook => {
- strats[hook] = mergeHook
- })
-
- // watch
- strats.watch = function (
- parentVal,
- childVal,
- vm,
- key
- ) {
- // work around Firefox's Object.prototype.watch...
- if (parentVal === nativeWatch) { parentVal = undefined; }
- if (childVal === nativeWatch) { childVal = undefined; }
- /* istanbul ignore if */
- if (!childVal) { return Object.create(parentVal || null) }
- {
- assertObjectType(key, childVal, vm);
- }
- if (!parentVal) { return childVal }
- var ret = {};
- extend(ret, parentVal);
- for (var key$1 in childVal) {
- var parent = ret[key$1];
- var child = childVal[key$1];
- if (parent && !Array.isArray(parent)) {
- parent = [parent];
- }
- ret[key$1] = parent
- ? parent.concat(child)
- : Array.isArray(child) ? child : [child];
- }
- return ret
- };

生命周期钩子和watch
被合并为一个数组,然后正序遍历一次执行
叠加型合并有:component
、directives
、filters
- strats.components=
- strats.directives=
-
- strats.filters = function mergeAssets(
- parentVal, childVal, vm, key
- ) {
- var res = Object.create(parentVal || null);
- if (childVal) {
- for (var key in childVal) {
- res[key] = childVal[key];
- }
- }
- return res
- }
叠加型主要是通过原型链进行层层的叠加
props
、methods
、inject
、computed
,就是将新的同名参数替代旧的参数data
, 通过set
方法进行合并和重新赋值watch
,原理是将函数存入一个数组,然后正序遍历依次执行component
、directives
、filters
,通过原型链进行层层的叠加从上面的例子看来,使用mixin的好处多多,但是凡是都有两面性,这里总结几点优缺点供大家参考:
mixin给我们提供了方便的同时也给我们带来了灾难,所以有很多时候不建议滥用它,但是在有些场景下使用它又是非常合适的,这就得根据自己来取舍了。所以在很多时候我们需要考虑用公共组件还是使用mixin。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。