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

  

  在我们实例化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)
}

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

 export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
// 检测组件名称是否合法
checkComponents(child)
} if (typeof child === 'function') {
child = child.options
}
// 格式化属性名称
normalizeProps(child, vm)
// 格式化依赖注入内容
normalizeInject(child, vm)
// 格式化指令内容
normalizeDirectives(child) // Apply extends and mixins on the child options,
// but only if it is a raw options object that isn't
// the result of another mergeOptions call.
// Only merged options has the _base property.
// 只有合并的options拥有_base属性
// 需要递归进行合并属性
// 首先合并extends和mixins
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) {
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)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
// 合并属性
function mergeField (key) {
// 获取属性的合并策略
const strat = strats[key] || defaultStrat
// 调用属性合并策略,返回值为属性合并结果
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}

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

  

  混入的实现,采用了策略模式的设计模式,在对组件数据初始化时,会遍历组件的配置文件,根据配置文件,调用对应的策略方法。如果组件中存在不是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构造方法中的合并策略,也就是全局定义一个合并策略,如下所示:

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

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

  

  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)的更多相关文章

  1. vue 混入 mixin,自定义指令,过滤器

    vue 混入 mixin ,分发 vue 组件中重复的功能 局部的书写格式 // mixin.js var mymixin = {  // 这是一个对象:对象里面的写法与组件里面的写法一模一样,组件该 ...

  2. vue混入 (mixin)的使用

    混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能.一个混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项. 使用示 ...

  3. vue混入mixin的使用,保证你看的明明白白!

    场景描述 有些时候,我们发现有些组件部分功能代码是几乎是一样的. 这个时候,我们就可以将相同的逻辑代码抽离出来 此时我们的主角混入mixin就登场了 下面我们有a-test和b-test两个组件,点击 ...

  4. 应用三:Vue之混入(mixin)与全局混入

    (注:本文适用于有一定Vue基础或开发经验的读者,文章就知识点的讲解不一定全面,但却是开发过程中很实用的) 首先介绍一下混入mixin的概念:   官方文档:混入提供了一种非常灵活的方式,来分发 Vu ...

  5. Vue之混入(mixin)与全局混入

    Vue之混入(mixin)与全局混入 接下来通过一个简单的例子看看混入的基础用法: 首先新建一个mixin.js文件,添加以下代码. let mixin = { data() { return { m ...

  6. Vue混入

    Vue 混入 混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式.混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项. 数据对象合 ...

  7. Vue 混入(mixins)

    详细参考官方文档 基础 混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式.混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项. ...

  8. vue混入(mixins)

    混入(mixins)是一种分发vue组件中可复用功能的非常灵活的方式.混入对象可以包含任意组件选项. 当组件使用混入对象时,所以混入对象的选项将被混入该组件本身选项,当组件和混入对象含有同名选项时,这 ...

  9. vue的mixin简化开发

    vue的mixin可以将多个组件公用的声明周期方法和数据封装成一个对象,在不同的组件中自由插拔.实际做项目的时候,可以定义一些mixin,供多个组件使用.也非常有必要定义一个全局的mixin对象,对所 ...

随机推荐

  1. 获取本机出口IP方法

    Windows 浏览器常用方式访问: 1.http://www.ip138.com/ 2.http://tool.chinaz.com/ 3.在百度搜索框内输入 ip  会自动识别出来当前的出口IP ...

  2. TCP/IP协议第一卷第一章

    1.链路层 链路层有时也称作数据链路层或网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡.它们一起处理与电缆(或其他任何传输媒介)的物理接口细节.把链路层地址和网络层地址联系起来 ...

  3. [转载]2.1 UiPath条件判断活动If的介绍和使用

    一.if的介绍 if语句是指编程语言(包括c语言.C#.Python.Java.汇编语言等)中用来判定所给定的条件是否满足,根据判定的结果(真或假)决定执行给出的两种操作之一. 二.if在UiPath ...

  4. 详解Kafka Producer

    上一篇文章我们主要介绍了什么是 Kafka,Kafka 的基本概念是什么,Kafka 单机和集群版的搭建,以及对基本的配置文件进行了大致的介绍,还对 Kafka 的几个主要角色进行了描述,我们知道,不 ...

  5. html5 svg实现不规则形状图片触发事件

    html5 svg实现不规则形状图片触发事件<pre><!DOCTYPE html><html lang="en"> <head> ...

  6. python中的集合、元组和布尔

    #元组,元组跟列表一样,只不过列表可读可写,而元组一般用来只读,不修改#python中不允许修改元组的数据,也包括不能删除其中的元素. t1 = ('a','b','c','d','s','a') & ...

  7. 关于RAID 10的介绍与创建

    一.RAID 10的简介 定义: RAID10也被称为镜象阵列条带.象RAID0一样,数据跨磁盘抽取:象RAID1一样,每个磁盘都有一个镜象磁盘, 所以RAID 10的另一种会说法是 RAID 0+1 ...

  8. webpack优化之玩转代码分割和公共代码提取

    前言 开发多页应用的时候,如果不对webpack打包进行优化,当某个模块被多个入口模块引用时,它就会被打包多次(在最终打包出来的某几个文件里,它们都会有一份相同的代码).当项目业务越来越复杂,打包出来 ...

  9. React组件间的通讯

    组件化开发应该是React核心功能之一,组件之间的通讯也是我们做React开发必要掌握的技能.接下来我们将从组件之间的关系来分解组件间如何传递数据. 1.父组件向子组件传递数据 通讯是单向的,数据必须 ...

  10. django_1:配置文件

    工程下: settings.py(建议设置成如下) DATABASES                                   #数据库配置 DEBUG = True           ...