专栏首页Super 前端Vue 组件扩展

Vue 组件扩展

最近,新项目架构搭建在扩展组件的场景中:图表使用了extends方式,而公共业务server和view之间使用了mixins方式。对于二者的选择,我们通常会解释为extends的优先级高于mixins,但其真实的差异是由于其合并策略不同或者说在合并策略中执行的顺序不同导致的 – 源码

下述,简单阐述Vue中涉及到组件扩展的相关API及自定义合并策略等相关内容

定义公共基础的组件配置模板,下述示例使用

const BaseComponent = {
    name: 'BaseComponent',
    template: `<div>{{message}}</div>`,
    data () {
        return {
            message: 'My is BaseComponent!'
        }
    },
    created () {
        console.log(`BaseComponent ${this.message}`)
    }
}

Vue.extend( options )

返回的是一个“扩展实例构造器”,也就是一个预设了部分选项的 Vue 实例构造器,方便创建可复用的组件。参数是一个包含组件选项的对象,其中data必须为函数。

<div id="app"></div>
<script>
    const HelloWorld = Vue.extend(BaseComponent)
    // 创建 HelloWorld 实例,并挂载到一个元素上。
    new HelloWorld().$mount('#app')
</script>

Vue.extend 是构造一个组件的语法器。可以作用到 Vue.component 中;也可以作用到 vue 实例或者某个组件中的 components 属性。

示例1:作用于 Vue.component 中,其为全局注册,也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 ( new Vue ) 的模板中

Vue.component('myBase', BaseComponent)

示例2: 作用于某组件中

const base = Vue.extend(BaseComponent)

上述两者都可以生效:

<div id="app">
	<my-base></my-base>
    <base-component></base-component>
</div>
<script>
	new Vue({
		el: '#app',
	  	components: {
	    	baseComponent: base
	  	}
	})
</script>

Vue.component() & Vue.extend(options) 区别:

前者创建的是组件(组件是可复用的 Vue 实例),后者创建的是组件构造器。Vue.component('my-component', { /* ... */ }) 注册组件,传入一个选项对象 (自动调用 Vue.extend)

// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))
// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
// 获取已注册的组件 (始终返回构造器)
var MyComponent = Vue.component('my-component')

Vue.mixin( mixin )

全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。使用恰当时,可以为自定义对象注入处理逻辑。但不推荐在应用代码中使用。因为会影响到每个单独创建的 Vue 实例 (包括第三方模板)!

Vue.mixin({
	created () {
	  var myOption = this.$options.myOption
	  if (myOption) {
	    console.log(myOption)
	  }
	}
})
new Vue({
	myOption: 'hello!'
})

vm.$options 用于当前 Vue 实例的初始化选项。需要在选项中包含自定义属性时会有用处。

new Vue({
	customOption: 'foo',
   	created: function () {
    	console.log(this.$options.customOption) // => 'foo'
   	}
})

同时,其也可以获取Vue内部的一些初始选项,如在自动销毁的vue event Bus可以使用到:

// 当前调用组件的 destroyed 钩子
const destroyed = vm.$options.destroyed
// 为当前组件destroyed钩子增加销毁方法
!destroyed.includes(destroyHandler) && destroyed.push(destroyHandler)

extends

允许声明扩展另一个组件(可以是一个简单的选项对象或构造函数),而无需使用 Vue.extend。这主要是为了便于扩展单文件组件。

const vm = new Vue({
    el: '#app',
    extends: BaseComponent,
    data () {
        return {
            message: 'My is Mixins!'
        }
    },
    created: function () {
        console.log(`mixins ${this.message}`)
    }
})
# DOM展示
My is Mixins! 
# 控制台输出结果:
BaseComponent My is Mixins!
mixins My is Mixins!

mixins

接受一个混入对象的数组。这些混入实例对象可以像正常的实例对象一样包含选项,他们将在 Vue.extend() 里最终选择使用相同的选项合并逻辑合并。

const vm = new Vue({
    el: '#app',
    mixins: [BaseComponent],
    data () {
        return {
            message: 'My is Mixins!'
        }
    },
    created: function () { 
       console.log(`mixins ${this.message}`)
    }
})

输出结果和extends一致!!!

extends和mixins的区别

let myMixin = {
    data () {
        return {
            message: 'local mixin'
        }
    },
    created () {
        console.log('local mixin')
    }
}
let myExtend = {
    data () {
        return {
            message: 'local extend'
        }
    },
    created () {
        console.log('local extend')
    }
}
new Vue({
    el: '#app',
    extends: myExtend,
    mixins: [myMixin],
    template: `<div>{{message}}</div>`,
    created () {
        console.log('current Vue instance')
    }
})
# DOM展示
local mixin
# 控制台输出结果
Vue.mixin
local extend
local mixin
current Vue instance

通过Vue的 源码 可知,extends 优先于 mixins 进行合并,所以会先执行 extends 的 created,但是对于相同的data属性,后面的会覆盖前面的!这些都是由合并策略所决定的!!!

合并策略

/**
 * Default strategy.
 */
const defaultStrat = function (parentVal: any, childVal: any): any {
  return childVal === undefined
    ? parentVal
    : childVal
}

上述代码为Vue源码中关于策略合并的默认配置。子组件为主,子组件存在则使用子组件的,否则使用父组件的!!!

下述根据Vue源码,说明各个属性的合并策略,具体可以通过后面的链接查看Vue源码!

  • options.el 合并策略就是默认的合并策略,即以子组件的选项为主,子组件的选项不存在时,才使用父组件的。源码地址
  • options.data 子组件没有响应的属性则使用父组件的,否则子组件的会覆盖父组件的! 源码地址
  • options.props options.methods options.computed 先拓展的是 parentVal 对象,然后再拓展 childVal 对象,存在相同的 childVal 会覆盖 parentVal 值。源码地址
  • options.hook 父组件和子组件都设置了钩子函数选项,那么它们会合并到一个数组里,而且父组件的钩子函数会先执行,最后返回一个合并后的数组。 源码地址
  • options.components options.directives options.filters 合并的策略就是返回一个合并后的新对象,新对象的自有属性全部来自 childVal,但是通过原型链委托在了 parentVal 上。事实上,和 defaultStrat 一个道理。源码地址
  • options.watch 相同属性会做合并处理,父组件在前,子组件在后。源码地址

自定义合并策略

通过查看 源码,我们可以发现所有的策略都是依赖strats进行的,也就是基于config.optionMergeStrategies完成的。

/**
 * 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

Vue提供了自定义合并策略的选项的API,optionMergeStrategies 合并策略选项分别接收在父实例和子实例上定义的该选项的值作为第一个和第二个参数,Vue 实例上下文被作为第三个参数传入。

示例: 修改默认合并策略

new Vue({
    el: '#app',
    mixins: [{
        created () {
            console.log(`BaseComponent`)
        }
    }],
    created () {
        console.log('this instance')
    }
})

由上述总结的默认合并策略可知,hook 会合并到一个数组中,且父组件优先执行,因此输出结果为:

BaseComponent
this instance

修改合并策略

Vue.config.optionMergeStrategies.created = function (parentVal, childVal) {
    return Array.isArray(childVal) ? childVal : [childVal]
}

再次执行上述代码,可发现只输出 this instance 。合并策略已被修改!对于Vue内部定义的策略强烈不建议进行修改!!!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Vue-第三方库扩展

    ​ 之前写过一篇 Vue-组件扩展 的文章,通常我们采用 extends 或 mixins 进行扩展组件;但项目中我们经常还会使用一些第三方库(Lodash...

    奋飛
  • Vue基础:响应式

    Vue不是框架(前端框架往往需要解决路由、试图管理、数据持久化等流程),Vue只关注视图层。

    奋飛
  • iBatis搭建JAVA项目

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

    奋飛
  • Vue实战系列—项目搭建四步曲(0)

    开篇:想通过输出Vue系列文章,让大家全面理解Vue的实现原理,掌握实用技巧,能在实战中使用Vue,解锁一个开发技能。

    前端大彬哥
  • 2019 Vue开发指南:你都需要学点啥?

    如果您是Vue开发的新手,您可能已经听过很多关于它的专业术语了,例如:单页面应用程序、异步组件、服务器端呈现等。

    Javanx
  • Angular.js和Vue.js 深度对比

    转载请注明出自:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。

    前端博客 : alili.tech
  • 从一个奇怪的错误出发理解 Vue 基本概念

    有人在学习 Vue 过程中遇到一个奇怪的问题,并为之迷惑不已——为什么这么简单的一个项目都会出错。

    前端博客 : alili.tech
  • 【Vue源码探究一】当我们引入Vue,我们引入了什么?

    从api文档中我们可以了解到,当我们引入vue.js, 我们仅仅引入了一个构造函数(Vue) 引入了构造函数后,我们有几种使用方式

    前端博客 : alili.tech
  • 拥抱 Vue 3 系列之 JSX 语法

    “别再更新了,学不动了”。这句话不知道出了多少开发者的辛酸。在过去的一年中,Vue 团队一直都在开发 Vue.js 的下一个主要版本,就在 6 月底,尤大更新同...

    政采云前端团队
  • Vue3 对 Web 应用性能的改进[每日前端夜话0xE1]

    有关即将发布的 Vue.js 的第 3 个主要版本的信息越来越多。通过下面的讨论,虽然还不能完全确定其所有内容,但是我们可以放心地认为,它将是对当前版本(已经非...

    疯狂的技术宅

扫码关注云+社区

领取腾讯云代金券