- mixin是什么
- Vue中的mixin
- 局部混入
- 全局混入
- 注意事项:
- 使用场景
- 源码分析
- Vue 的几种类型的合并策略
- 替换型
- 合并型
- 队列性
- 叠加型
- 小结
- Vue中的mixin
此文章,来源于印客学院的资料,这里只是分享,便于查漏补缺。
mixin是什么
Mixin 是 面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问 mixin 类的方法而不必成为其子类
Mixin 类通常作为功能模块使用,在需要该功能时“混入”,有利于代码复用又避免了多继承的复杂
Vue中的mixin
先来看一下官方定义
mixin (混入) ,提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能
本质其实就是一个 js 对象
,它可以包含我们组牛中任意功能选项,如 data、components、methods、created、computed 等等
我们只要将共用的功能以对象的方式传入 mixi选项中,当组件使用mixins对象时所有 mixins 对象的选项都将被混入该组件本身的选项中来
在 Vue2 中,Mixin 混入的生命周期函数与组件的生命周期函数相同
在 Vue 中我们可以局部混入
跟全局混入
更多详细内容,请微信搜索“前端爱好者
“, 戳我 查看 。
局部混入
定义一个 mixin 对象,有组件 options 的 data、methods 属性
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
组件通过 mixins 属性调用 mixin 对象
Vue.component('componentA',{
mixins: [myMixin]
})
该组件在使用的时候,混合了 mixin 里面的方法,在自动执行 created生命钩子,执行 hello 方法
全局混入
通过 Vue.mixin()
进行全局的混入
Vue.mixin({
created: function () {
console.log("全局混入")
}
})
使用全局混入需要特别注意,因为它会影响到每一个组件实例 (包括第三方组件)
PS: 全局混入常用于插件的编写
注意事项:
- 当组件存在与 mixin 对象相同的选项的时候,进行递归合并的时候组件的选项会覆盖 mixin 的选项
- 但是如果相同选项为生命周期钩子的时候,会合并成一个数组,先执行 mixin 的钩子,再执行组件的钩子
使用场景
在日常的开发中,我们经常会遇到在不同的组件中乡常会需要用到一些相同或者相似的代码,这些代码
的功能相对独立
这时,可以通过 Vue 的 mixin 功能将相同或者相似的代码提出来
举个例子
定义一个modal 弹窗组件,内部通过 isShowing 来控制显示
const Modal = {
template: '#modal',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
}
}
定义一个 tooltip 提示框,内部通过 isShowing 来控制显示
const Tooltip = {
template: '#tooltip',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
}
}
通过观察上面两个组件,发现两者的逻辑是相同,代码控制显示也是相同的,这时候 mixin 就派上用场了
首先抽出共同代码,编写一个 mixin
const toggle = {
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
}
}
两个组件在用上,只需要引入 mixin
const Modal = {
template: '#modal',
mixins: [toggle]
};
const Tooltip = {
template: '#tooltip',
mixins: [toggle]
}
通过上面小小的例子,让我们知道了 Mixin 对于封装一些可复用的功能如此有趣、方便、实用
源码分析
首先从 Vue.mixin 入手
源码位置: /src/core/global-api/mixin.js
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
主要是调用 mergeOptions 方法
源码位置: /src/core/util/options.js
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (child.mixins) { // /判断有没有mixin 也就是mixin里面挂mixin的情况 有的话递归进行合并
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
const options = {}
let key
for (key in parent) {
mergeField(key) // 先遍历parent的key 调对应的strats[XXX]方法进行合并
}
for (key in child) {
if (!hasOwn(parent, key)) { // 如果parent已经处理过某个key 就不处理了
mergeField(key) // 处理child中的key 也就parent中没有处理过的key
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key) // 根据不同类型的options调用strats中不同的方法进行合并
}
return options
}
从上面的源码,我们得到以下几点:
- 优先递归处理 mixins
- 先遍历合并 parent 中的 key ,调用 mergeField 方法进行合并,然后保存在变量 options
- 再遍历 child,合并补上 parent 中没有的 key ,调用 mergeField 方法进行合并,保存在变量 options
- 通过 mergeField 函数进行了合并
Vue 的几种类型的合并策略
下面是关于 Vue 的几种类型的合并策略
- 替换型
- 合并型
- 队列型
- 叠加型
替换型
替换型合并有 props、methods 、inject 、computed
同名的 props 、methods 、inject 、computed 会被后来者代替
合并型
和并型合并有: data
mergeData 函数遍历了要合并的 data 的所有属性,然后根据不同情况进行合并:
- 当目标 data 对象不包含当前属性时,调用 set 方法进行合并 (set方法其实就是一些合并重新赋值的方法)
- 当目标 data 对象包含当前属性并且当前值为纯对象时,递归合并当前对象值,这样做是为了防止对象存在新增属性
队列性
队列性合并有: 全部生命周期和 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 被合并为一个数组,然后正序遍历一次执行
叠加型
叠加型有:directives、 filters、component
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 ,通过原型链进- 行层层的叠加