Recently, I encountered a strange problem when I was working on a project. A method injected into Vue instance through vue. mixin method did not work. Later, after careful investigation, I found that the instance implemented a method with the same name, which led to the failure of vue. mixin injection method. A later search revealed that the methods injected into the instance by vue.mixin were replaced by methods of the same name in the instance, rather than being executed in sequence. So I had the idea of looking at the source code, and this article was born
The source version used in this article is 2.2.6
Mixin: SRC /core/global-api/mixin.js: SRC /core/global-api/mixin.js: SRC /core/global-api/mixin.js
SRC /core/util/options.js; SRC /core/util/options.js; SRC /core/util/index.js/SRC /core/util/index.js/SRC /core/util/index.js/SRC /core/util/index.js/SRC /core/util/index.js
Find the mergeOptions method in options.js as follows:
Its main process is roughly as follows:
- In a non-production environment, this parameter is invoked first
checkComponents
Check the validity of the passed parameters, and we’ll talk about the implementation later. - call
normalizeProps
Methods andnormalizeDirectives
Method to normalize the two attributes. - Check if the passed parameter has one
extends
Attribute to extend other Vue instancesThe official documentation. Why check this property here? Because when an incoming object has this property, it means that all Vue instances extend the instance it specifies (Vue.mixin
So, before we merge, we need to mergeextends
To merge, ifextends
Is a Vue constructor (or possibly an extended Vue constructor), then the merge parameter becomes itoptions
Option; Otherwise just mergeextends
. - After checking the parameters passed in
extends
After the property, we also check itmixins
Property, a functional reference for this propertyThe official documentation. Because if the incoming Vue configuration object is still specifiedmixins
So, we need to recursively merge. - Once you’ve done that, you can start merging the pure
mixin
The parameters. You can see throughmergeField
Function merges, first traverses the merged target object, merges; It then iterates over the object to be merged, merging only properties that do not exist on the target object. That’s where the focus of the merger comes inmergeFiled
The function.
Continue with the mergeField function:
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
Copy the code
This function uses the key value to select the specific function to merge in Strats. This is a typical strategy pattern, so let’s see how Strats defines it.
The definition of strats in options.js is as follows:
/** * Option overwriting strategies are functions that handle * how to merge a parent option value and a child option * value into the final value. */
const strats = config.optionMergeStrategies
Copy the code
The config object comes from SRC /core/config.js, which defines all the types and initial values of config. Of course, the initial values are empty arrays, so we need to see the implementation in options.js.
The following sections explain the different merging methods according to the configuration properties of Vue.
A, el
El merges in a simpler way because of itself
The source code is as follows:
/** * Options with restrictions */
if(process.env.NODE_ENV ! = ='production') {
strats.el = strats.propsData = function (parent, child, vm, key) {
if(! vm) { warn(`option "${key}" can only be used during instance ` +
'creation with the `new` keyword.')}return defaultStrat(parent, child)
}
}
Copy the code
As you can see, the strats.el method and the propsData method (propsData document) are defined only in the development environment. This is because these two properties are special, especially since propsData is only used in the development environment for testing purposes. Another quirk is that both can only exist in Vue instances constructed by the new operator calling the Vue constructor, so a warning pops up when the VM is not passed.
The merge method for both attributes is defaultStrat, whose source code is as follows:
/** * Default strategy. */
const defaultStrat = function (parentVal: any, childVal: any) :any {
return childVal === undefined
? parentVal
: childVal
}
Copy the code
You can see that parentVal is directly replaced when childVal is defined.
This method will be used later.
Second, the data
The merging of the data option is critical because data is a function in a child component that returns a special responsive object.
The source code is as follows:
There are two cases, one is that vm parameters are passed, the other is that vm parameters are not passed.
When no VM parameters are passed, childVal needs to be verified as a function, while parentVal does not need to be verified as it must be a function to pass the previous merge verification to get to this step. Once you have determined that they are both functions, you call them and then process the two returned data objects through mergeData, which we’ll cover later.
When a VM parameter is passed, it needs to be handled in a different way. When a function is passed, it uses the return value to do the next merge. When it is another value, use its value directly for the next merge.
This step verifies that childVal and parentVal are functions. And because this step checks, we don’t need to check any more in this case. Why?
We can look back at the mergeOptions source and see that the third parameter VM is optional. It passes vm to itself during recursion. This results in that when we first call mergeOptions and pass VM, all subsequent recursions pass VM. When we didn’t pass the VM value at first, all subsequent recursions didn’t pass the VM parameter. Whether there is a VM or not depends on whether the argument we passed when we called the function in the first place contains vm.
Global lookup of the mergeOptions function call, you can see two places:
- The first is located
src/core/instance/init.js
, the file is also definedinitMixin
Merges the configuration object passed to the Vue constructor into vm.$options. In this case, the VM is passed with the value of the Vue instance currently being constructed. - The second one is what WE’ve been talking about
src/core/global-api/mixin.js
This is where the global API is defined.
In short, when the Vue constructor constructs a Vue instance, it calls mergeOptions and passes the VM instance as the third argument; The VM is not passed when we call vue. mixin for global obfuscation. The former corresponds to the second, and the latter to the first.
When we construct the Vue instance first, the VM is passed to perform the second case, parentVal is validated, so the first case is no longer validated when we call Vue. Mixin later.
When we call Vue. Mixin first without instantiating Vue, the code in the first case is executed, so does that cause a bug? The answer must be no, because parentVal is undefined, because vue. mixin calls vue. options as the initial value of parentVal, and this object contains no data property at all.
MergeData = mergeData; mergeData = mergeData;
As you can see, all the attributes of the data to be merged are iterated, and then merged as the case may be:
- When the target data object does not contain the current property, the call
set
Method to merge, more on that laterset
. - When the target data object contains the current attributes and the current values are pure objects, the current object values are recursively merged to prevent the object from having new attributes.
Moving on to the set function:
You can see that set also handles two cases for Target. We first determine that target is an array, and then assign directly if target contains the current property. The next step is to check whether target is a responsive object. If it is, a warning will pop up in the development environment. It is best not to let data return a responsive object, because it is a waste of performance. If it is not a reactive object it can be returned directly, otherwise the target is further converted to a reactive object and dependencies are collected.
If an instance specifies a data value with the same name as a mixins, then the mixin data will be invalid. If both are objects but the mixin has new attributes, the mixin data will be invalid. It will still be added to instance data.
Life Cycle.
The merge function of the Hooks is defined as mergeHook, and the source code is as follows:
/** * Hooks and props are merged as arrays. */
function mergeHook (parentVal: ? Array
, childVal: ? Function | ? Array
): ?Array<Function> {
return childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
}
Copy the code
This one is simpler, and the code comments are clear: the lifecycle hooks of the Vue instance are merged into an array. Which hooks can be combined in SRC /core/config.js:
/** * List of lifecycle hooks. */
_lifecycleHooks: [
'beforeCreate'.'created'.'beforeMount'.'mounted'.'beforeUpdate'.'updated'.'beforeDestroy'.'destroyed'.'activated'.'deactivated'].Copy the code
Combining assets (components, filters, directives) is also simple and is skipped below.
Fourth, the watch
Merged watch function source code is as follows:
The source code is also very simple, and the comments are very clear. Just like the lifecycle hooks, vue. mixin will merge all the watches with the same name into an array and execute them one by one when triggered.
5. Props, methods, and computed tomography
All three merge using the same strategy, source code below:
The processing here is also relatively simple. It can be seen that when vue. mixin is confused by multiple calls, props, methods, and computed with the same names will be replaced by later ones. But when a Vue constructor passes a property of the same name, the configuration object that the constructor accepts takes precedence. Because mergeOptions is also called when Vue is instantiated, the second argument is the configuration object accepted by the Vue constructor, as described earlier.
6. Some auxiliary functions
There are several auxiliary functions such as checkComponents, normalizeProps, normalizeDirectives. Here is a simple paste of the source code:
checkComponents
This function checks for compliance with the components property, mainly to prevent custom components from using built-in HTML tags.
normalizeProps
This function mainly decorates the props property. This includes converting props from an array of strings to objects, and formatting all props.
normalizeDirectives
This function also mainly formats the cache property to reorganize the original object into a new one in the standard format.
7. Customize the merge strategy
See Vue official documentation: custom option merge strategy, it allows us to custom merger strategy, the specific way is to replace the Vue. Config. OptionsMergeStrategies, namely the definition mentioned above mentioned in the SRC/core/config. The properties of js. We can also look at the source code, which is defined in initGlobalAPI in SRC /core/global-api/index.js.
const configDef = {}
configDef.get = (a)= > config
if(process.env.NODE_ENV ! = ='production') {
configDef.set = (a)= > {
warn(
'Do not replace the Vue.config object, set individual fields instead.')}}Object.defineProperty(Vue, 'config', configDef)
Copy the code
You can see that the last sentence defines a config property for the Vue function, whose property is defined as configDef. It is not allowed to set its value in production, but in development we can set vue.config directly. Then by setting the Vue. Config. OptionsMergeStrategies, we can change the merger strategy, in the back and then to merge, reads the properties in the config object, then you can use our custom merged merge strategy.
Eight, summary
After looking at how these attributes are combined, you have some idea of how vue.mixin works. Personally, I think we can basically divide vue. mixin into three types of attribute combination, one is substitution, one is combination, and one is queue.
Alternatives include EL, props, methods, and computed, in which new parameters replace old ones.
The merge type has data, which is the kind of behavior where newly passed parameters are merged into old parameters.
Queue merges have watches, all lifecycle hooks, actions like that where all arguments are merged into an array and retrieved as necessary.
We also need to be careful when using vue. mixins, especially substitutive merge properties. When you specify the same name in mixins, do not specify the same name in mixins, otherwise the properties in your mixins will be replaced and invalid.
The author’s level is limited, the article inevitably has flaws, please correct.