mixin机制 vue_vue源码解析-插件入侵机制

插件:聪明的程序员往往希望能更高(tou)效(lan)的完成指定的工作,插件就是按照一定的封装方式,暴露接口。让我们利用这些接口更快捷的实现功能。升职加薪。每个框架都提供了插件的扩展机制。这是框架可扩展性必不可少的一个部分。插件机制越简单。对于框架的生态的发展大有好处。jquery提供了$.fn.extend,angular有对应的依赖注入,module机制。既然vue那么精美,能迅速火起来。插件这部分的可扩展性必须顶级。这里接下来我们看看vue插件的入侵机制。

说到插件。我们最多使用的一个方法。无非就是 Vue.use(MyPlugin, { someOption: true });

这么说的话,这个方法应该是所有插件入侵vue的起点。没错。那么我们来看看这个方法:

Vue.use = function (plugin) {

/* istanbul ignore if */

if (plugin.installed) {

return//假如插件已经初始化过就不再继续。避免插件重复入侵

}

// additional parameters

var args = toArray(arguments, 1);//获取插件的配置参数

args.unshift(this);

if (typeof plugin.install === 'function') {

plugin.install.apply(plugin, args);//调用的是插件的install方法;

} else if (typeof plugin === 'function') {

plugin.apply(null, args);//若插件本省就是一个函数。则直接调用该函数

}

plugin.installed = true;

return this

};

Vue.use这个方法让我们知道来。插件入侵的起点是调用插件自身的install函数。那么不同的插件入侵的机制有些时候很不一样。我们可以知道。这个不一样肯定发生在install函数中。我们来看看官方的install函数中的一些方式:

MyPlugin.install = function (Vue, options) {

// 1. 添加全局方法或属性

Vue.myGlobalMethod = function () {

// 逻辑...

}

// 2. 添加全局资源

Vue.directive('my-directive', {

bind (el, binding, vnode, oldVnode) {

// 逻辑...

}

...

})

// 3. 注入组件

Vue.mixin({

created: function () {

// 逻辑...

}

...

})

// 4. 添加实例方法

Vue.prototype.$myMethod = function (options) {

// 逻辑...

}

}

我们按官网推荐的四种例子。来看看每种方法对应的源码:

1:Vue.myGlobalMethod = function () {

// 逻辑...

}

类似jquery中的jquery.myGlobalMethod或则$.myGlobalMethod简单来说就是给Vue这个全局对象添加一些工具方法。可以供全局快捷调用。我们这里就略过了

2: // 2. 添加全局资源

Vue.directive('my-directive', {

bind (el, binding, vnode, oldVnode) {

// 逻辑...

}

...

})

Vue.directive,Vue.filter,Vue.component等价。当全局使用这些api时。会在vue上把这些指令过滤器组件等放在相应的属性数组里。形如:

Vue.options = {

components: {

},

directives: {},

filters: {},

_base: Vue

}

因为他挂在全局的vue中。在vue初始化。调用init方法时。会执行:

vm.$options = mergeOptions(

resolveConstructorOptions(vm.constructor),//策略合并核心函数。可以仔细去看看

options || {},

vm

);

vue在创建实例时。会把vue对象上的options的对象中的属性提取出来和传入的options做合并。这里涉及到合并策略。以后会专门讲一下。这里只要知道。vue每个配置相都有自己的合并规则。mergeOptions会根据合并的类目去选择对应的合并规则。这里的component.directive.filter根据合并规则。Vue对象上的全局的这些属性会被放在实例的__proto__上。

同样的。相应的子组件。可以回过头去看一下组件那一章。在render创建子组件的时候。代码如下:

function createComponent (

Ctor,

data,

context,

children,

tag

) {

if (isUndef(Ctor)) {

return

}

var baseCtor = context.$options._base;

// plain options object: turn it into a constructor

if (isObject(Ctor)) {

Ctor = baseCtor.extend(Ctor);

}

// if at this stage it's not a constructor or an async component factory,

// reject.

if (typeof Ctor !== 'function') {

{

warn(("Invalid Component definition: " + (String(Ctor))), context);

}

return

}

// async component

if (isUndef(Ctor.cid)) {

Ctor = resolveAsyncComponent(Ctor, baseCtor, context);

if (Ctor === undefined) {

// return nothing if this is indeed an async component

// wait for the callback to trigger parent update.

return

}

}

// resolve constructor options in case global mixins are applied after

// component constructor creation

resolveConstructorOptions(Ctor);//核心:这里会再次合并一下vue上的全局的一些指令或则组件或则过滤器到组件的构造函数上

data = data || {};

// transform component v-model data into props & events

if (isDef(data.model)) {

transformModel(Ctor.options, data);

}

// extract props

var propsData = extractPropsFromVNodeData(data, Ctor, tag);

// functional component

if (isTrue(Ctor.options.functional)) {

return createFunctionalComponent(Ctor, propsData, data, context, children)

}

// extract listeners, since these needs to be treated as

// child component listeners instead of DOM listeners

var listeners = data.on;

// replace with listeners with .native modifier

data.on = data.nativeOn;

if (isTrue(Ctor.options.abstract)) {

// abstract components do not keep anything

// other than props & listeners

data = {};

}

// merge component management hooks onto the placeholder node

mergeHooks(data);

// return a placeholder vnode

var name = Ctor.options.name || tag;

var vnode = new VNode(

("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')),

data, undefined, undefined, undefined, context,

{ Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }

);

return vnode

}

最后在内部组件初始化时。vue会调用

function initInternalComponent (vm, options) {

var opts = vm.$options = Object.create(vm.constructor.options);//这里还是把构造函数的options放在了$options上供后续使用

// doing this because it's faster than dynamic enumeration.

opts.parent = options.parent;

opts.propsData = options.propsData;

opts._parentVnode = options._parentVnode;

opts._parentListeners = options._parentListeners;

opts._renderChildren = options._renderChildren;

opts._componentTag = options._componentTag;

opts._parentElm = options._parentElm;

opts._refElm = options._refElm;

if (options.render) {

opts.render = options.render;

opts.staticRenderFns = options.staticRenderFns;

}

}

总的来说。如果是全局的指令过滤器时。vue统一把他放在根构造方法上。根实例初始化时。通过策略合并合并到$options中。而子组件稍微绕了一下。最终也是放在$options的原型上。很连贯啊。这样只要是全局的组件。指令过滤器。每个子组件都可以继承使用。达到了插件的效果。

3:下面来看看mixin方法:

Vue.mixin({

created: function () {

// 逻辑...

}

...

})

Vue.mixin = function (mixin) {

this.options = mergeOptions(this.options, mixin);

};

//这里还是不可避免要看看mergeOptions函数:

function mergeOptions (

parent,

child,

vm

) {

{

checkComponents(child);

}

if (typeof child === 'function') {

child = child.options;

}

normalizeProps(child);

normalizeDirectives(child);

var extendsFrom = child.extends;

if (extendsFrom) {

parent = mergeOptions(parent, extendsFrom, vm);

}

if (child.mixins) {

for (var i = 0, l = child.mixins.length; i < l; i++) {

parent = mergeOptions(parent, child.mixins[i], vm);

}

}

var options = {};

var key;

for (key in parent) {

mergeField(key);

}

for (key in child) {

if (!hasOwn(parent, key)) {

mergeField(key);

}

}

function mergeField (key) {

var strat = strats[key] || defaultStrat;

options[key] = strat(parent[key], child[key], vm, key);

}

return options

}

分两种情况吧:

a:全局注册时即vue.mixin时。直接调用合并。直接便利mixin中的项目。分别调用相应合并策略。合并到构造函数的options中。影响后面所有的子组件

b:局部注册时。

if (child.mixins) {

for (var i = 0, l = child.mixins.length; i < l; i++) {

parent = mergeOptions(parent, child.mixins[i], vm);

}

}

则会去递归的调用合并策略把该合并的项目合并结束为止;

vue.mixin就相当于是一个传入的额外的配置项目,会让vue重新按照规则合并一次,成功入侵vue

4:

// 4. 添加实例方法

Vue.prototype.$myMethod = function (options) {

// 逻辑...

}

这个方法就很明显了。在vue的原型上挂载方法。vue的实例自然而然就能继承。子组件在创建的时候。

Sub.prototype = Object.create(Super.prototype);

Sub.prototype.constructor = Sub;

将原型指向根构造函数Vue的prototype;自然而然就会有Vue的原型上的所有属性和方法。。

以上就是vue比较常用的插件侵入方法。哈哈。下次再说。告辞

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值