【Vue面试题十四】、说说你对vue的mixin的理解,有什么应用场景?

news2024/9/29 3:22:15

文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。

面试官:说说你对vue的mixin的理解,有什么应用场景?

在这里插入图片描述

一、mixin是什么

Mixin是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问mixin类的方法而不必成为其子类

Mixin类通常作为功能模块使用,在需要该功能时“混入”,有利于代码复用又避免了多继承的复杂

Vue中的mixin

先来看一下官方定义

mixin(混入),提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。

本质其实就是一个js对象,它可以包含我们组件中任意功能选项,如datacomponentsmethodscreatedcomputed等等

我们只要将共用的功能以对象的方式传入 mixins选项中,当组件使用 mixins对象时所有mixins对象的选项都将被混入该组件本身的选项中来

Vue中我们可以局部混入跟全局混入

局部混入

定义一个mixin对象,有组件optionsdatamethods属性

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的钩子,再执行组件的钩子

二、使用场景

在日常的开发中,我们经常会遇到在不同的组件中经常会需要用到一些相同或者相似的代码,这些代码的功能相对独立

这时,可以通过Vuemixin功能将相同或者相似的代码提出来

举个例子

定义一个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
  }
}

主要是调用merOptions方法

源码位置:/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的几种类型的合并策略

  • 替换型
  • 合并型
  • 队列型
  • 叠加型

替换型

替换型合并有propsmethodsinjectcomputed

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

同名的propsmethodsinjectcomputed会被后来者代替

合并型

和并型合并有: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 的所有属性,然后根据不同情况进行合并:

  • 当目标 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被合并为一个数组,然后正序遍历一次执行

叠加型

叠加型合并有:componentdirectivesfilters

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
}

叠加型主要是通过原型链进行层层的叠加

小结

  • 替换型策略有propsmethodsinjectcomputed,就是将新的同名参数替代旧的参数
  • 合并型策略是data, 通过set方法进行合并和重新赋值
  • 队列型策略有生命周期函数和watch,原理是将函数存入一个数组,然后正序遍历依次执行
  • 叠加型有componentdirectivesfilters,通过原型链进行层层的叠加

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1073418.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

abap代码优化和性能调优工具

select/end select与loop类似是循环&#xff08;一个是对数据库表&#xff0c;一个是对内表&#xff09; select *效率低于select 字段1 字段2... select 主键1 主键2 主键3 非主键4效率高于select 主键1 主键2 非主键4. into table 内表效率高于into corresponding fields …

Linux动态链接库.so文件

一、动态库和静态库的区别 库是一个二进制文件&#xff0c;包含的代码可以被程序调用&#xff0c;如标准库、线程库。Windows 和 Linux下的库文件格式不兼容。 Windows环境&#xff1a;静态库是 .lib 文件&#xff0c;共享库是 .dll 文件 Linux环境&#xff1a;静态库是 .a 文…

IC工程师职场必备《经典Verilog100多个代码案例》(附下载)

对于IC行业的人员而言&#xff0c;Verilog是最基础的入门&#xff0c;用于数字电路的系统设计&#xff0c;很多的岗位都会用到&#xff0c;可对算法级、门级、开关级等多种抽象设计层次进行建模。 Verilog由于其简单的语法&#xff0c;和C语言的相似性&#xff0c;目前被各大公…

uni-app:js时间与时间戳之间的转换

运行结果 代码 <template><view></view> </template><script>export default {data() {return {}},onLoad() {//时间转换为时间戳&#xff08;十位时间戳即秒级时间戳&#xff09;const time 2023-10-01 12:34:56; // 时间格式为年-月-日 时:分…

BlowFish加解密原理与代码实现

BlowFish加解密原理与代码实现 一丶简介 ​ BlowFish 是一个对称加密的加密算法。由Bruce Schneier&#xff0c;1993年设计的。是一个免费自由使用的加密算法。 了解的必要知识 BlowFish是一个对称区块加密算法。每次加密数据为 64位 &#xff08;2个int)类型数据大小。八个…

Maven 引入外部依赖

如果我们需要引入第三方库文件到项目&#xff0c;该怎么操作呢&#xff1f; pom.xml 的 dependencies 列表列出了我们的项目需要构建的所有外部依赖项。 要添加依赖项&#xff0c;我们一般是先在 src 文件夹下添加 lib 文件夹&#xff0c;然后将你工程需要的 jar 文件复制到 …

mac(M1)卸载miniconda3

参考https://stackoverflow.com/questions/29596350/how-to-uninstall-mini-conda-python step1 因为我目前只有一个base环境&#xff0c;所以直接在这个环境中安装 anaconda-clean即可 conda install anaconda-clean然后继续输入 anaconda-clean如果不加–yes&#xff0c;那…

精品Python考务监考管理系统

《[含文档PPT源码等]精品Python考务管理系统的设计与实现》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程等&#xff01; 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;python 使用框架&#xff1a;Django 前端技术&#xff1a;JavaSc…

SECS/GEM封装库RapidSecs开发手记HMSM

四. SECS/GEM封装库RapidSecs开发手记-基础库开发-1 https://blog.csdn.net/jxb_memory/article/details/105739410 secsgem https://www.cnpython.com/pypi/secsgem

95740-26-4|用于体内DNA合成的探针F-ara-EdU

产品简介&#xff1a;(2S)-2-Deoxy-2-fluoro-5-ethynyluridine&#xff0c;一种用于体内DNA合成的探针&#xff0c;其毒性比EdU和BrdU都小。当需要延长细胞存活时间和不受干扰的细胞周期进展时&#xff0c;非常适合进行代谢DNA标记。 CAS号&#xff1a;95740-26-4 分子式&…

2023腾讯云双11优惠活动服务器价格预测

2023腾讯云服务器双十一有活动吗&#xff1f;有的&#xff0c;双11轻量应用服务器和云服务器CVM均有活动&#xff0c;只是现在还未开启&#xff0c;一般为11月1日开始&#xff0c;活动时间持续1个月时间&#xff0c;腾讯云百科txybk.com预测一下2023年腾讯云服务器双十一优惠活…

链表常见问题

1.实现一个链表 public class Linked {public Node first;public Node last;//头插public void addFirst(int data){//获取原头节点Node f first;//创建新节点Node newNode new Node(data);//指向新头节点first newNode;//if(f null){last newNode;}else {newNode.next …

POI 和 EasyExcel 操作 Excel

一、概述 目前操作 Excel 比较流行的就是 Apache POI 和阿里巴巴的 easyExcel。 1.1 POI 简介 Apache POI 是用 Java 编写的免费开源的跨平台的 Java API&#xff0c;Apache POI 提供 API 给 Java 程序对 Microsoft Office 格式文档读和写的常用功能。POI 为 “Poor Obfuscati…

【工作流引擎】Activiti的使用01

基础配置 依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM…

Promise resolver undefined is not a function

错误的代码 function handlUsernameOrEmail(rules,value,){const regex /^([a-zA-Z0-9_-])([a-zA-Z0-9_-])(\.[a-zA-Z0-9_-]){1,2}$/;if(regex.test(value)){return new Promise().resolve(成功);}else {return new Promise().reject(失败);}// console.log(rules,rules,valu…

图文详解六十道Java并发

图文详解六十道Java并发 基础 1.并行跟并发有什么区别? 从操作系统的角度来看,线程是CPU分配的最小单位。 并行就是同一时刻,两个线程都在执行。这就要求有两个CPU去分别执行两个线程。并发就是同一时刻,只有一个执行,但是一个时间段内,两个线程都执行了。并发的实现依…

三、机器学习基础知识:Python常用机器学习库(Numpy第一部分)

文章目录 1、Numpy定义2、ndarray对象3、Numpy数据类型4、Numpy数组类型 1、Numpy定义 Numpy是Numberical Python的简称&#xff0c;是用来进行高性能计算与分析的基础包&#xff0c;是Python中重要的扩充库。它支持高维度数组与矩阵运算&#xff0c;也针对数组运算提供了大量…

gradle依赖导入,jar方式

新项目使用了gardle方式导入依赖&#xff0c;但是很不巧我都没有听过这个东西&#xff0c;然后就紧急的学了学&#xff0c;发现其实也听见的&#xff0c;然后准备动手试一下 问题 导入dm数据库驱动&#xff0c;换了好几种写法也不大行&#xff0c;然后发现我之前的项目也是使…

IDERA ER/Studio Data Professional 19.3.5 Crack

IDERA ER/Studio 数据架构师&#xff1f; 它能够从用户的单个界面创建和管理多个数据库平台的数据模型。信息建模者和架构师希望对与小型企业需求相关的不同数据高度做出反应。有些关键行动可能需要他们的关注。 特点&#xff1a; 构建数据模型作为增长周期的一部分。发现并记…

Redis的C客户端(hiredis库)使用

文章目录 1、Ubuntu安装redis服务端2、hiredis库的安装3、同步API接口的使用3.1、连接redis数据库redisConnect3.2、发送需要执行的命令redisCommand3.3、redisCommandArgv函数3.4、redisAppendCommand*函数支持管道命令3.5、释放资源3.6、同步连接代码 3.7、异步连接4、redis连…