浅析vue混入(mixin)
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)的更多相关文章
- vue 混入 mixin,自定义指令,过滤器
vue 混入 mixin ,分发 vue 组件中重复的功能 局部的书写格式 // mixin.js var mymixin = { // 这是一个对象:对象里面的写法与组件里面的写法一模一样,组件该 ...
- vue混入 (mixin)的使用
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能.一个混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项. 使用示 ...
- vue混入mixin的使用,保证你看的明明白白!
场景描述 有些时候,我们发现有些组件部分功能代码是几乎是一样的. 这个时候,我们就可以将相同的逻辑代码抽离出来 此时我们的主角混入mixin就登场了 下面我们有a-test和b-test两个组件,点击 ...
- 应用三:Vue之混入(mixin)与全局混入
(注:本文适用于有一定Vue基础或开发经验的读者,文章就知识点的讲解不一定全面,但却是开发过程中很实用的) 首先介绍一下混入mixin的概念: 官方文档:混入提供了一种非常灵活的方式,来分发 Vu ...
- Vue之混入(mixin)与全局混入
Vue之混入(mixin)与全局混入 接下来通过一个简单的例子看看混入的基础用法: 首先新建一个mixin.js文件,添加以下代码. let mixin = { data() { return { m ...
- Vue混入
Vue 混入 混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式.混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项. 数据对象合 ...
- Vue 混入(mixins)
详细参考官方文档 基础 混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式.混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项. ...
- vue混入(mixins)
混入(mixins)是一种分发vue组件中可复用功能的非常灵活的方式.混入对象可以包含任意组件选项. 当组件使用混入对象时,所以混入对象的选项将被混入该组件本身选项,当组件和混入对象含有同名选项时,这 ...
- vue的mixin简化开发
vue的mixin可以将多个组件公用的声明周期方法和数据封装成一个对象,在不同的组件中自由插拔.实际做项目的时候,可以定义一些mixin,供多个组件使用.也非常有必要定义一个全局的mixin对象,对所 ...
随机推荐
- [考试反思]0929csp-s模拟测试55:消逝
菜得过分. 面对T1的大板子不知所措,然后T2的贪心不小心把排序语句删了... T1这种大模板啊...其实我是觉得我能打出来的,然后先用一个小时码了一个2k. 然后做T2想贪心就出来了.十分钟码完T3 ...
- 「考试」 Or
不得不说是一道多项式神题了. 虽然说颓代码颓的很厉害不过最终A掉了. 好好讲一讲这道题. 涉及的知识点是:高阶导数,NTT,指数型母函数,泰勒公式,以及意志力和数学推导能力. 那就开始了. 一个测试点 ...
- CSPS模拟 85
WWB大佬的bitset映射真是太强了! %%% T1 观察样例,猜规律. T2 对题目的翻译工作用了很长时间 翻译错了好几次.. 观察到奇环没法染色,选的边必须把奇环弄断 如果在偶环上,偶环就变得没 ...
- NOIP模拟 32
我在31反思中膜拜过了B哥 没想到这次又... 我给老姚家丢脸了...STO 首先T1暴力就写挂了... 贪图从$n^3$*$2^n$优化成$n^2$*$2^n$然后打错了 哗哗的扔分 而且正解都想不 ...
- 浅谈OI中的底层优化!
众所周知,OI中其实就是算法竞赛,所以时间复杂度非常重要,一个是否优秀的算法或许就决定了人生,而在大多数情况下,我们想出的算法或许并不那么尽如人意,所以这时候就需要一中神奇的的东西,就是底层优化: 其 ...
- [LINQ2Dapper]最完整Dapper To Linq框架(五)---查看Linq实际执行的SQL
此例子是使用LINQ2Dapper封装,效率优于EntityFramwork,并且支持.NetFramework和.NetCore框架,只依赖于Dapper支持.net framework4.6.1及 ...
- PHP创建文件命名中文乱码解决的方法
PHP创建文件命名中文乱码解决的方法 <pre>iconv('utf-8', 'gbk', $dir); </pre> 因为系统环境是gbk 所以里面的字符也要gbk 编码一致 ...
- 【algo&ds】3.栈和队列
1.堆栈 堆栈(Stack):具有一定操作约束的线性表(只在一端(栈顶,Top)做插入.删除) 先进后出特性 1.1堆栈的抽象数据类型描述 类型名称: 堆栈(Stack) 数据对象集:一个有0个或多个 ...
- 2019CSP游记
\(CSP2019\)游记 写在前面 考完,终于深刻地认识到省一似乎和我想象的真不是一个难度.也罢,不然为什么\(NOIP\)改了名还是这么有含金量. 考前一天和一群同学们嚷嚷着要去吃散伙饭,说没拿到 ...
- 微信web协议,群成员唯一uin,获取群成员唯一标识
群成员唯一标识获取接口 全网最新,支持调试测试.觉得OK再付款! 800元出售源码 不讲价 联系QQ:2052404477