Vue.extend和VueComponent的關(guān)系源碼解析
前言
寫Vue.js已經(jīng)3年了吧,對Vue.extend全局方法的了解也就停留在了解啦,一直抱著不用就不學(xué)的思想,造成了一次又一次的錯過~~~
直到最近,才通過公司10年禿頭少年的代碼,才知道錯過Vue.extend方法,也就等于錯過了對Vue組件的第二春了解的機(jī)會?。?!
需求分析
在頁面中彈出消息提示框,比如在用戶點(diǎn)擊按鈕給予回應(yīng)
這種業(yè)務(wù)場景很常見吧,肯定是先創(chuàng)建一個彈框組件,在需要使用彈框的地方注冊組件并掛載到頁面上去
但是需求還沒結(jié)束,又提出在所以頁面都可以彈出這個消息提示框
首選局部注冊肯定是不行了,局部注冊只能當(dāng)前頁面使用,所以要選擇在App.vue中注冊并掛載,因?yàn)锳pp.vue是所有組件的根組件,所以組件也都能訪問到App.vue中的方法,我們只需要在App.vue暴露一個彈框顯示的方法就可以了
產(chǎn)品還不打算放過我,又提出需求是可以同時出現(xiàn)多個消息提示框
這個就不好實(shí)現(xiàn)了呀,一個組件掛載只能彈出一個提示框,總不能在App.vue掛載很多個吧,其實(shí)這也是解決方案,但是不利于擴(kuò)展、占用內(nèi)存!?。?/p>
那就沒有辦法了嗎?也不是,那就要請出主角Vue.extend全局方法?。?!
Vue.extend
看看官方的八股文:
使用基礎(chǔ) Vue 構(gòu)造器,創(chuàng)建一個“子類”。參數(shù)是一個包含組件選項(xiàng)的對象
我認(rèn)為這段文字表達(dá)十分明了,首先Vue.extend參數(shù)是包含組件選項(xiàng)的對象,返回值是Vue的子類
import Vue from 'vue'
let VueComponent = Vue.extend({})
VueComponent.prototype.__proto__ == Vue.prototype // true
我們這里將子類稱為VueComponent
你對這個名稱很熟悉吧~~~
import Vue from 'vue/dist/vue.esm.js' // 必須引入這個版本的Vue才可以使用template選項(xiàng)
new Vue({
el: "#app",
template: `
<App></App>
`,
components: { App },
mounted() {
let app = this.$children[0]; // App組件實(shí)例
let VueComponent = app.constructor // App構(gòu)造函數(shù)
console.log(app);
console.log(VueComponent);
console.log(VueComponent.prototype.__proto__ == Vue.prototype);
}
})

不難發(fā)現(xiàn)VueComponent就是組件的構(gòu)造函數(shù)~~~
是不是一下就明白了,Vue.extend的返回值就是組件的構(gòu)造函數(shù),傳入的選項(xiàng)就是組件的初始配置
Vue.extend作用可以理解為:
首選創(chuàng)建一個Vue類的子類(組件的構(gòu)造函數(shù)),將傳入的組件選項(xiàng)配置當(dāng)做實(shí)例子類的默認(rèn)值(組件的初始配置),并返回這個類(組件的構(gòu)造函數(shù))
Vue.component("Toast", {}) // 全局注冊組件
console.log(Vue.component("Toast")); // Toast組件構(gòu)造函數(shù)

在使用Vue.component方法注冊全局組件時,其實(shí)內(nèi)部也是先調(diào)用Vue.extend生成VueComponent,再聲明到全局組件中,局部組件也是一樣的~~~
其實(shí)這里還能了解一個知識點(diǎn):所以的組件構(gòu)造函數(shù)的父類都是Vue,所以組件可以使用Vue上所以方法和屬性,比如emit、set等
編程式的使用組件
VueComponent可以被稱為小Vue,VueComponent的使用也就可以對照Vue
let VueComponent = Vue.extend({})
new VueComponent({
el: "#app",
template: `<h1>Vue.extend的使用</h1>`
})

是不是和Vue的使用一模一樣喃?
區(qū)別在于Vue.extend可以通過參數(shù)確定組件的初始配置~~~
現(xiàn)在完成一下最開始的需求吧~
編寫my-toast.vue
<template>
<h1 class="title" v-if="show">{{ title }}</h1>
</template>
<script>
export default {
name: "my-toast",
props: {
title: {
typeof: String,
default: ""
}
},
data() {
return {
show: true
};
},
created() {
setTimeout(() => {
this.show = false;
this.$nextTick(() => {
this.$destroy();
});
}, 2000);
}
};
</script>
<style>
.title {
width: 180px;
height: 50px;
position: fixed;
right: 20px;
top: 20px;
background-color: rgba(0, 0, 0, 0.15);
text-align: center;
line-height: 50px;
border-radius: 8px;
font-size: 16px;
}
</style>
在vue的原型上綁定顯示彈框方法
import Vue from 'vue';
import myToast from './components/my-toast.vue';
Vue.prototype.$toast = function (title) {
const ToastComponent = Vue.extend(myToast);
const toastComponent = new ToastComponent({
propsData: {
title
}
})
toastComponent.$mount(document.createElement('div'));
document.body.appendChild(toastComponent.$el);
}
在需要彈框的地方調(diào)用方法
// ....某vue頁面
mounted() {
this.$toast("測試");
}
let vm = new VueComponent(options)的注意點(diǎn):
- 傳遞的props配置需要配置到propsData屬性中
- 使用VueX、
VueRoter等第三方模塊,需要把這些模塊掛載到options上 - 使用插槽需要通過vm.$scopedSlots傳遞
源碼分析
你可以在源碼目錄src/core/global-api/extend.js下找到這個函數(shù)的定義
Vue.extend = function (extendOptions: any): typeof Component {
extendOptions = extendOptions || {}
// this指的是Vue
const Super = this
// 每個組件構(gòu)造函數(shù)都有唯一的cid
const SuperId = Super.cid
// 創(chuàng)建完的VueComponent構(gòu)造函數(shù),會保存到_Ctor中
// 如果下次傳入相同的extendOptions和SuperId,就直接取用緩存內(nèi)的值
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
// 獲取組件name
const name =
getComponentName(extendOptions) || getComponentName(Super.options)
if (__DEV__ && name) {
// 校驗(yàn)組件name
validateComponentName(name)
}
// 創(chuàng)建VueComponent構(gòu)造函數(shù)
const Sub = function VueComponent(this: any, options: any) {
this._init(options)
} as unknown as typeof Component
// 重點(diǎn):實(shí)現(xiàn)了原型鏈繼承,讓VueComponent的原型指向Vue的原型
// Vue的原型的所有方法和屬性,在VueComponent也存在
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
// 生成唯一cid
Sub.cid = cid++
// 傳入的extendOptions和Vue.options合并
Sub.options = mergeOptions(Super.options, extendOptions)
Sub['super'] = Super
// 配置props的響應(yīng)式
if (Sub.options.props) {
initProps(Sub)
}
// 配置computed的響應(yīng)式
if (Sub.options.computed) {
initComputed(Sub)
}
// 繼承靜態(tài)方法
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// ASSET_TYPES = ['component', 'directive', 'filter']
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
if (name) {
Sub.options.components[name] = Sub
}
// 保存基礎(chǔ)配置
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// 設(shè)置緩存
cachedCtors[SuperId] = Sub
return Sub
}
Vue.extend關(guān)鍵在于繼承Vue,拋開其他功能代碼,主要實(shí)現(xiàn)繼承的代碼:
// 繼承原型
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
// 繼承靜態(tài)方屬性和方法
// 繼承靜態(tài)方法
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// ASSET_TYPES = ['component', 'directive', 'filter']
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})以上就是Vue.extend和VueComponent的關(guān)系源碼解析的詳細(xì)內(nèi)容,更多關(guān)于Vue.extend和VueComponent的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vite前端構(gòu)建Turborepo高性能monorepo方案
這篇文章主要為大家介紹了vite前端構(gòu)建Turborepo高性能monorepo方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
el-date-picker設(shè)置日期默認(rèn)值兩種方法(當(dāng)月月初至月末)
這篇文章主要給大家介紹了關(guān)于el-date-picker設(shè)置日期默認(rèn)值(當(dāng)月月初至月末)的相關(guān)資料,文中通過代碼示例將解決的辦法介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08
基于Vue3+TypeScript實(shí)現(xiàn)圖片預(yù)覽組件
在現(xiàn)代的 Web 應(yīng)用中,圖片預(yù)覽是一個常見的需求,本文將介紹如何使用 Vue3 和 TypeScript 開發(fā)一個圖片預(yù)覽組件,支持展示單張或多張圖片,并提供了豐富的配置選項(xiàng),需要的朋友可以參考下2024-04-04
vue+springmvc導(dǎo)出excel數(shù)據(jù)的實(shí)現(xiàn)代碼
這篇文章主要介紹了vue+springmvc導(dǎo)出excel數(shù)據(jù)的實(shí)現(xiàn)代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-06-06

