浅析vue混入(mixin)

摘要:
在vue中混合可以在一定程度上提高代码的可重用性。vue混合的基本流程如图所示。混合属性的合并发生在组件生命周期的钩子调用之前。当我们实例化Vue时,我们主要调用Vue_Init方法,该方法主要用于初始化组件状态和事件。具体代码如下:Vue.prototype _ init=function(options?=='production'&&config.performance&&mark){startTag=`Vue perf start:${vm.uid}`endTag=`Vue perf end:${vm.uid}` mark}//aflagtoavoidthisbeingobservedvm._IsVue=true//合并选项//合并属性,确定初始化的组件是否为{//optimizeinternalcomponentsinstantiation//sincedynamiccoptionsmergespottyflow,并且//internalcompomponentsoptions都不需要特殊处理。initInternalComponent}else{//合并vue属性vm.$options=mergeOptions}/*istanbuiligentrelelse*/if(process.env.NODE_env!=='production'){configDef.set=()=˃{warn}对象。defineProperty/exposed utilmethods//注意:除非您了解风险,否则请不要将此内容视为公开API的一部分。Vue公司。util={warn,extend,mergeOptions,defineAreactive}Vue。set=setVue。delete=delVue。nextTick=nextTick//2.6explicitoobservableAPIVue.observable=:T=˃{observerturnbj}//初始化Vue构造函数的选项,使用组件的初始属性direct、fi FiltsVue。options=对象。createASSET_TYPES.forEach//用于识别Weex多实例场景中的“基本”构造或扩展对象//组件。//_基本属性Vue。options_Base=Vueextend//初始化vue。useapiinitUse//Initialize mixinapiinitMixin//Initialize extendapinitExtend//初始化组件、指令、filtinitAssetRegister}mixin实现的主要代码如下:1exportfunctionmergeOptions(2paret:Object,3child:Object,4vm?

  vue中的混入,可以在一定程度上提高代码的复用性。通俗来说,混入类似于“继承”,当前组件对象继承于组件对象,一般情况下遵循“就近原则”。但是与继承不同的是,继承一般都跟随着属性的重写与合并,混入在不同的配置项中,有着不同的混入策略,下面会一一进行介绍vue不同配置项的混入策略。vue混入的基本流程如图所示,混入属性的合并是发生在组件的生命周期钩子调用之前的。

  浅析vue混入(mixin)第1张

   在我们实例化vue时,主要是调用Vue._init方法,此方法,主要功能是初始化组件状态、事件,具体代码如下:

Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

    let startTag, endTag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    // 合并属性,判断初始化的是否是组件
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      // 合并vue属性
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      // 初始化proxy拦截器
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    // 初始化options
    initLifecycle(vm)
    // 初始化组件事件侦听
    initEvents(vm)
    // 初始化渲染方法
    initRender(vm)
    callHook(vm, 'beforeCreate')
    // 初始化依赖注入内容,在初始化data、props之前
    initInjections(vm) // resolve injections before data/props
    // 初始化props/data/method/watch/methods
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }
    // 挂载元素
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }

  mergeOptions是合并组件的配置项,第一次实例化Vue时调用,接收两个参数,第一个参数是构造函数默认自带的属性,在项目初始化时会调用initGlobalAPI方法,会在Vue构造函数上初始化一些默认的配置,具体代码如下所示,第二个为我们实例化vue配置的属性

// 初始化全局API
export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () => config
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  Object.defineProperty(Vue, 'config', configDef)

  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }
  // 初始化Vue构造函数的options,初始属性为components,directives,filters
  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  // _base属性
  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)
  // 初始化vue.use api
  initUse(Vue)
  // 初始化mixins api
  initMixin(Vue)
  // 初始化extend api
  initExtend(Vue)
  // 初始化component,directive,filter
  initAssetRegisters(Vue)
}

  混入实现的主要代码如下:

 1 export function mergeOptions (
 2   parent: Object,
 3   child: Object,
 4   vm?: Component
 5 ): Object {
 6   if (process.env.NODE_ENV !== 'production') {
 7     // 检测组件名称是否合法
 8     checkComponents(child)
 9   }
10 
11   if (typeof child === 'function') {
12     child = child.options
13   }
14   // 格式化属性名称
15   normalizeProps(child, vm)
16   // 格式化依赖注入内容
17   normalizeInject(child, vm)
18   // 格式化指令内容
19   normalizeDirectives(child)
20 
21   // Apply extends and mixins on the child options,
22   // but only if it is a raw options object that isn't
23   // the result of another mergeOptions call.
24   // Only merged options has the _base property.
25   // 只有合并的options拥有_base属性
26   // 需要递归进行合并属性
27   // 首先合并extends和mixins
28   if (!child._base) {
29     if (child.extends) {
30       parent = mergeOptions(parent, child.extends, vm)
31     }
32     if (child.mixins) {
33       for (let i = 0, l = child.mixins.length; i < l; i++) {
34         parent = mergeOptions(parent, child.mixins[i], vm)
35       }
36     }
37   }
38 
39   const options = {}
40   let key
41   for (key in parent) {
42     mergeField(key)
43   }
44   for (key in child) {
45     if (!hasOwn(parent, key)) {
46       mergeField(key)
47     }
48   }
49   // 合并属性
50   function mergeField (key) {
51     // 获取属性的合并策略
52     const strat = strats[key] || defaultStrat
53     // 调用属性合并策略,返回值为属性合并结果
54     options[key] = strat(parent[key], child[key], vm, key)
55   }
56   return options
57 }

  具体某个字段的合并,调用的是mergeField方法,此方法主要是获取代码混入策略,返回值作为混入的结果。通过调试我们可以看出,混入的策略对象中包含我们常见的vue属性,如下所示:

  浅析vue混入(mixin)第2张

   混入的实现,采用了策略模式的设计模式,在对组件数据初始化时,会遍历组件的配置文件,根据配置文件,调用对应的策略方法。如果组件中存在不是vue指定的配置,就是策略类strats中不包含的属性,就会调用默认的合并方法defaultStrat,该方法的定义如下:

/**
 * Default strategy.
 * 默认的属性合并策略,采用就近原则,如果子级没有,就采用父级的
 */
const defaultStrat = function (parentVal: any, childVal: any): any {
  return childVal === undefined
    ? parentVal
    : childVal
}

  strats是从哪来的呢? 属性合并策略strats默认会读取config中的配置文件,在项目初始化时,首先会初始化全局的api,将config文件挂载到Vue构造方法中,再者会初始化所有的属性合并策略,如下图所示:

 浅析vue混入(mixin)第3张

  浅析vue混入(mixin)第4张

    如果我们要自定义属性合并策略,只需要覆盖掉Vue构造方法中的合并策略,也就是全局定义一个合并策略,如下所示:

// 自定义属性合并策略
Vue.config.optionMergeStrategies.methods = function (toVal, fromVal) {
    if (toVal && fromVal) return fromVal
    if (toVal) return toVal
    if (fromVal) return fromVal
}

    vue对组件配置项的每一部分执行的合并策略有所差异,每一项的合并策略,主要分为覆盖、保留,如下所示:

  浅析vue混入(mixin)第5张

   data的混入返回的是一个function,参数保持对父级的引用,在初始化state的时候会调用,data的混入策略如下:

// 定义data的属性合并策略
strats.data = function (
  parentVal: any,
  childVal: any,
  vm?: Component
): ?Function {
  if (!vm) {
    if (childVal && typeof childVal !== 'function') {
      process.env.NODE_ENV !== 'production' && warn(
        'The "data" option should be a function ' +
        'that returns a per-instance value in component ' +
        'definitions.',
        vm
      )

      return parentVal
    }
    return mergeDataOrFn(parentVal, childVal)
  }

  return mergeDataOrFn(parentVal, childVal, vm)
}
/**
 * Data
 * 合并对象 or 数组
 * 利用闭包,返回一个包含私有参数的执行方法
 */
export function mergeDataOrFn (
  parentVal: any,
  childVal: any,
  vm?: Component
): ?Function {
  if (!vm) {
    // in a Vue.extend merge, both should be functions
    // 在Vue.extend场景中???
    if (!childVal) {
      return parentVal
    }
    if (!parentVal) {
      return childVal
    }
    // when parentVal & childVal are both present,
    // we need to return a function that returns the
    // merged result of both functions... no need to
    // check if parentVal is a function here because
    // it has to be a function to pass previous merges.
    return function mergedDataFn () {
      return mergeData(
        typeof childVal === 'function' ? childVal.call(this, this) : childVal,
        typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal
      )
    }
  } else {
    return function mergedInstanceDataFn () {
      // instance merge
      const instanceData = typeof childVal === 'function'
        ? childVal.call(vm, vm)
        : childVal
      const defaultData = typeof parentVal === 'function'
        ? parentVal.call(vm, vm)
        : parentVal
      if (instanceData) {
        return mergeData(instanceData, defaultData)
      } else {
        return defaultData
      }
    }
  }
}

   

免责声明:文章转载自《浅析vue混入(mixin)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Linux 关于安装rabbitmq以及开启远程访问权限步骤Latex学习(载入图片并居中)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Virtual Box虚拟机下CentOS网络设置

VirtualBox中有4中网络连接方式: a. NAT 网络地址转换模式(Network Address Translation)b. Bridged Adapter 桥接模式c. Internal 内部网络模式d. Host-only Adapter 主机模式 VMWare中有三种,其实他跟VMWare 的网络连接方式都是一样概念,只是比VMWare多...

关于Microsoft Virtual PC 2007安装使用

Virtual PC 和 VMWare 都是世界顶级虚拟机软件,使用了当今最先进的虚拟技术。前者为微软出品,自然对Microsoft Windows具有天然的亲和力。安装运行虚拟机,可以完成多项任务,可谓好处多多。下面,以Microsoft Virtual PC 2007为例(兼容Vista),对网友提出的问题进行综合答复—— 1。运行虚拟操作系统,在硬件上...

DataFrame和python中数据结构互相转换

楔子 有时候DataFrame,我们不一定要保存成文件、或者入数据库,而是希望保存成其它的格式,比如字典、列表、json等等。当然,读取DataFrame也不一定非要从文件、或者数据库,根据现有的数据生成DataFrame也是可以的,那么该怎么做呢?我们来看一下 DataFrame转成python中的数据格式 转成json DataFrame转成json,...

Android_照相机Camera_调用系统照相机返回data为空

本博文为子墨原创,转载请注明出处! http://blog.csdn.net/zimo2013/article/details/16916279 1.调用系统照相机 [java] view plaincopy   //实例化一个intent,并指定action   Intent intent = new Intent(MediaStore.ACT...

Ubuntu16桥接模式上网并设置静态ip

今天想运行老师发的代码,虚拟机又抽风了,网连不上,ping也主机也ping不通,没办法又忙活半天重新配置了一遍网络这方面,为了方便下次再配置,记录一下方法步骤: 首先设置将虚拟机设置为桥接模式   接下来查看主机的ip,例如我的windows系统,就win+r,接下来输入cmd。命令行输入ipconfig查看本机ip   这样就获取到主机的ip,子网掩码和...

kvm安装

需要准备的有 1.环境:Centos7 2.准备虚拟机硬盘 3.需要系统iso镜像 (这里用到的是CentOS-7-x86_64-Minimal-1810.iso) 4vnc 5xming 一. 准备环境 1.关闭防火墙和selinux [root@ localhost ~]# systemctl stop firewalld [root@ localh...